# API reference

Every fiscalclock operation, what it returns, and how to call it in Python, JavaScript and Rust.

This page lists the operations of API version 1 and how each language names them. The normative contract, with every argument rule and error path, is the [API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/); the Rust crate's full API is on [docs.rs](https://docs.rs/fiscalclock/0.1.0/fiscalclock/).

Every operation is defined once, in the Rust core. The Python and npm packages convert their native values to the core's JSON API and back, so they return the same results as the crate.

Each example uses the definition `retail-4-5-4.json` from [the quick start](https://fiscalclock.karthik.nz/start-here/quick-start/), and prints exactly the output shown under it.

## Names in each language

| Operation | Python | JavaScript | Rust |
| - | - | - | - |
| Build a calendar | `FiscalCalendar` | `new FiscalCalendar` | `FiscalCalendar::from_json` |
| [`validate`](https://fiscalclock.karthik.nz/reference/api/#validate) | `validate` | `validate` | `FiscalCalendar::from_json` |
| [`locate`](https://fiscalclock.karthik.nz/reference/api/#locate) | `locate` | `locate` | `locate` |
| [`period`](https://fiscalclock.karthik.nz/reference/api/#period) | `period` | `period` | `period` |
| [`shift`](https://fiscalclock.karthik.nz/reference/api/#shift) | `shift` | `shift` | `shift` |
| [`parent`](https://fiscalclock.karthik.nz/reference/api/#parent) | `parent` | `parent` | `parent` |
| [`children`](https://fiscalclock.karthik.nz/reference/api/#children) | `children` | `children` | `children` |
| [`years`](https://fiscalclock.karthik.nz/reference/api/#years) | `years` | `years` | `years` |
| [`format`](https://fiscalclock.karthik.nz/reference/api/#format) | `format` | `format` | `format` |
| [`parse`](https://fiscalclock.karthik.nz/reference/api/#parse) | `parse` | `parse` | `parse` |
| [`dimension`](https://fiscalclock.karthik.nz/reference/api/#dimension) | `dimension` | `dimension` | `dimension` |
| [`dimension_csv`](https://fiscalclock.karthik.nz/reference/api/#dimension_csv) | `dimension_csv` | `dimensionCsv` | `dimension_csv` |
| [`explain`](https://fiscalclock.karthik.nz/reference/api/#explain) | `explain` | `explain` | `explain` |

All operations except `validate` are methods of a calendar, and take their arguments in the same order in every language, as the examples below show. Optional arguments are keyword arguments with a default of `None` in Python, optional parameters in JavaScript, and `Option` values in Rust, where `period` takes `Option<u32>` and `format` takes `Option<&str>`. In Rust, periods are passed as `&Period`, levels as `Level` and dates as `Date`.

In Python and JavaScript, a definition is JSON text or the equivalent dict or object. A `level` is one of `"year"`, `"quarter"`, `"period"` and `"week"`; in Rust it is a `Level`. A `period` argument is a period returned by another operation, or just its coordinates `level`, `year` and `index`: the core recomputes the period from them ([API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/#operations)). Dates follow [Dates and intervals](https://fiscalclock.karthik.nz/guides/dates-and-intervals/#calendar-dates-only).

## Values

A **period** has `level`, `year` (the fiscal year number under the default scheme), `index` (1-based within the year, and empty for a year), `start`, `end_exclusive`, `last_day`, `days`, `weeks` (empty unless the year is week-based) and `has_extra_week`. In Python it is a frozen dataclass with `datetime.date` fields, in JavaScript a plain object with ISO date strings, and in Rust a `Period` struct whose `last_day()` is a method ([API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/#period)).

A **location** has the `date`, the `year`, `quarter`, `period` and `week` periods that contain it, and its 1-based `day_of_year`, `day_of_quarter`, `day_of_period` and `day_of_week` ([API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/#location)).

## Errors

A failed operation raises `FiscalclockError` in Python (a subclass of `ValueError`), throws `FiscalclockError` in JavaScript, and returns an `Err` in Rust. Every error has a stable `code`, the JSON `path` of the offending field or argument, a `message` and, where one helps, a `hint`. Some codes carry `details`, such as the candidates of an ambiguous label. In Python and JavaScript, the error's attributes describe the first error and `errors` holds them all; in Rust, building a calendar returns every error as `Errors`, and the other operations return one `Error`.

The codes, and the operations that raise each one, are listed in the [error catalogue](https://fiscalclock.karthik.nz/reference/spec/api-v1/#error-catalogue).

## The JSON API

Each package also exposes the core's JSON API, which takes and returns JSON text exactly as the core produces it: `call_json` and `validate_json` in Python, `callJson` and `validateJson` in JavaScript, and `fiscalclock::json::call` and `fiscalclock::json::validate` in Rust. For the same definition and arguments, the three return the same bytes ([API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/)).

## validate

Checks a definition and reports every error it finds, sorted by path and then by code. For a valid definition, Python and JavaScript return `ok`, the `calendar_id` and the canonical form; Rust's `from_json` returns the calendar, whose `calendar_id()` and `canonical_json()` give the same values. The Python and JavaScript `validate` functions return the errors instead of raising them. [Write a definition](https://fiscalclock.karthik.nz/guides/write-a-definition/#no-defaults-for-your-organisations-choices) shows an invalid definition.

**Python**

*validate.py*

```python
from pathlib import Path

from fiscalclock import validate

result = validate(Path("retail-4-5-4.json").read_text(encoding="utf-8"))
print(result["ok"], result["calendar_id"])
```

*Output*

```txt
True efc8868a4b19eaab
```

**JavaScript**

*validate.mjs*

```js
import { readFileSync } from "node:fs";
import { validate } from "fiscalclock";

const result = validate(readFileSync("retail-4-5-4.json", "utf8"));
console.log(result.ok, result.calendar_id);
```

*Output*

```txt
true efc8868a4b19eaab
```

**Rust**

*validate.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::FiscalCalendar;

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;
    println!("{}", cal.calendar_id());
    Ok(())
}
```

*Output*

```txt
efc8868a4b19eaab
```

## locate

Returns the location of a date: the year, quarter, period and week that contain it, and its position in each.

**Python**

*locate.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

here = cal.locate("2024-02-03")
print(here.year.year, here.quarter.index, here.period.index, here.week.index)
print(here.day_of_year, here.day_of_quarter, here.day_of_period, here.day_of_week)
```

*Output*

```txt
2023 4 12 53
371 98 35 7
```

**JavaScript**

*locate.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const here = cal.locate("2024-02-03");
console.log(here.year.year, here.quarter.index, here.period.index, here.week.index);
console.log(here.day_of_year, here.day_of_quarter, here.day_of_period, here.day_of_week);
```

*Output*

```txt
2023 4 12 53
371 98 35 7
```

**Rust**

*locate.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let here = cal.locate("2024-02-03".parse()?)?;
    println!("{} {:?} {:?} {:?}", here.year.year, here.quarter.index, here.period.index, here.week.index);
    println!("{} {} {} {}", here.day_of_year, here.day_of_quarter, here.day_of_period, here.day_of_week);

    Ok(())
}
```

*Output*

```txt
2023 Some(4) Some(12) Some(53)
371 98 35 7
```

## period

Returns the period with the given coordinates. Leave out `index` for a year. Coordinates that name no period return `PERIOD_NOT_FOUND`.

**Python**

*period.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

q4 = cal.period("quarter", 2023, 4)
print(q4.start, q4.end_exclusive, q4.last_day, q4.days, q4.weeks, q4.has_extra_week)
```

*Output*

```txt
2023-10-29 2024-02-04 2024-02-03 98 14 True
```

**JavaScript**

*period.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const q4 = cal.period("quarter", 2023, 4);
console.log(q4.start, q4.end_exclusive, q4.last_day, q4.days, q4.weeks, q4.has_extra_week);
```

*Output*

```txt
2023-10-29 2024-02-04 2024-02-03 98 14 true
```

**Rust**

*period.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar, Level};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let q4 = cal.period(Level::Quarter, 2023, Some(4))?;
    println!(
        "{} {} {} {} {:?} {}",
        q4.start, q4.end_exclusive, q4.last_day(), q4.days, q4.weeks, q4.has_extra_week
    );

    Ok(())
}
```

*Output*

```txt
2023-10-29 2024-02-04 2024-02-03 98 Some(14) true
```

## shift

Returns the period `n` steps away at the same level, crossing fiscal years as needed. `n` may be negative.

**Python**

*shift.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

q4 = cal.period("quarter", 2023, 4)
for n in (1, -4):
    moved = cal.shift(q4, n)
    print(n, cal.format(moved), moved.start)
```

*Output*

```txt
1 FY2024 Q1 2024-02-04
-4 FY2022 Q4 2022-10-30
```

**JavaScript**

*shift.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const q4 = cal.period("quarter", 2023, 4);
for (const n of [1, -4]) {
  const moved = cal.shift(q4, n);
  console.log(n, cal.format(moved), moved.start);
}
```

*Output*

```txt
1 FY2024 Q1 2024-02-04
-4 FY2022 Q4 2022-10-30
```

**Rust**

*shift.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar, Level};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let q4 = cal.period(Level::Quarter, 2023, Some(4))?;
    for n in [1, -4] {
        let moved = cal.shift(&q4, n)?;
        println!("{} {} {}", n, cal.format(&moved, None)?, moved.start);
    }

    Ok(())
}
```

*Output*

```txt
1 FY2024 Q1 2024-02-04
-4 FY2022 Q4 2022-10-30
```

## parent

Returns the period one level up that contains the given one: a quarter's year, a period's quarter, and a week's period in a week-based year or its year in a fixed-date year. A year has no parent, and returns `INVALID_ARGUMENT`.

**Python**

*parent.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

week = cal.period("week", 2023, 53)
period = cal.parent(week)
quarter = cal.parent(period)
year = cal.parent(quarter)
print(cal.format(week), "->", cal.format(period), "->", cal.format(quarter), "->", cal.format(year))
```

*Output*

```txt
FY2023 W53 -> FY2023 P12 -> FY2023 Q4 -> FY2023
```

**JavaScript**

*parent.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const week = cal.period("week", 2023, 53);
const period = cal.parent(week);
const quarter = cal.parent(period);
const year = cal.parent(quarter);
console.log(cal.format(week), "->", cal.format(period), "->", cal.format(quarter), "->", cal.format(year));
```

*Output*

```txt
FY2023 W53 -> FY2023 P12 -> FY2023 Q4 -> FY2023
```

**Rust**

*parent.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar, Level};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let week = cal.period(Level::Week, 2023, Some(53))?;
    let period = cal.parent(&week)?;
    let quarter = cal.parent(&period)?;
    let year = cal.parent(&quarter)?;
    println!(
        "{} -> {} -> {} -> {}",
        cal.format(&week, None)?,
        cal.format(&period, None)?,
        cal.format(&quarter, None)?,
        cal.format(&year, None)?
    );

    Ok(())
}
```

*Output*

```txt
FY2023 W53 -> FY2023 P12 -> FY2023 Q4 -> FY2023
```

## children

Returns the periods at a level inside a period, in order. In a fixed-date year, weeks cross month boundaries, so asking for the weeks of a quarter or a period returns `LEVEL_NOT_NESTED`.

**Python**

*children.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

q4 = cal.period("quarter", 2023, 4)
for period in cal.children(q4, "period"):
    print(cal.format(period), period.weeks, period.has_extra_week)
```

*Output*

```txt
FY2023 P10 4 False
FY2023 P11 5 False
FY2023 P12 5 True
```

**JavaScript**

*children.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const q4 = cal.period("quarter", 2023, 4);
for (const period of cal.children(q4, "period")) {
  console.log(cal.format(period), period.weeks, period.has_extra_week);
}
```

*Output*

```txt
FY2023 P10 4 false
FY2023 P11 5 false
FY2023 P12 5 true
```

**Rust**

*children.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar, Level};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let q4 = cal.period(Level::Quarter, 2023, Some(4))?;
    for period in cal.children(&q4, Level::Period)? {
        println!("{} {:?} {}", cal.format(&period, None)?, period.weeks, period.has_extra_week);
    }

    Ok(())
}
```

*Output*

```txt
FY2023 P10 Some(4) false
FY2023 P11 Some(5) false
FY2023 P12 Some(5) true
```

## years

Returns the fiscal years numbered `first` to `last`, both included, at most 10,000 of them.

**Python**

*years.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

for year in cal.years(2021, 2024):
    print(year.year, year.start, year.weeks)
```

*Output*

```txt
2021 2021-01-31 52
2022 2022-01-30 52
2023 2023-01-29 53
2024 2024-02-04 52
```

**JavaScript**

*years.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

for (const year of cal.years(2021, 2024)) {
  console.log(year.year, year.start, year.weeks);
}
```

*Output*

```txt
2021 2021-01-31 52
2022 2022-01-30 52
2023 2023-01-29 53
2024 2024-02-04 52
```

**Rust**

*years.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    for year in cal.years(2021, 2024)? {
        println!("{} {} {:?}", year.year, year.start, year.weeks);
    }

    Ok(())
}
```

*Output*

```txt
2021 2021-01-31 Some(52)
2022 2022-01-30 Some(52)
2023 2023-01-29 Some(53)
2024 2024-02-04 Some(52)
```

## format

Returns the label of a period under the default scheme, or under the named scheme. A scheme with no template for the period's level returns `TEMPLATE_MISSING`, and an unknown scheme returns `SCHEME_NOT_FOUND`. See [Labels](https://fiscalclock.karthik.nz/guides/labels/).

**Python**

*format.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

week = cal.period("week", 2023, 53)
print(cal.format(week))
print(cal.format(cal.parent(week), "nrf"))
```

*Output*

```txt
FY2023 W53
FY2023 P12
```

**JavaScript**

*format.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const week = cal.period("week", 2023, 53);
console.log(cal.format(week));
console.log(cal.format(cal.parent(week), "nrf"));
```

*Output*

```txt
FY2023 W53
FY2023 P12
```

**Rust**

*format.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar, Level};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let week = cal.period(Level::Week, 2023, Some(53))?;
    println!("{}", cal.format(&week, None)?);
    println!("{}", cal.format(&cal.parent(&week)?, Some("nrf"))?);

    Ok(())
}
```

*Output*

```txt
FY2023 W53
FY2023 P12
```

## parse

Returns the period that a label names. A label that matches no template returns `LABEL_NOT_RECOGNISED`, and one that matches several periods returns `AMBIGUOUS_LABEL` with every candidate ([Labels](https://fiscalclock.karthik.nz/guides/labels/#ambiguous-labels)).

**Python**

*parse.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

week = cal.parse("FY2023 W53")
print(week.level, week.year, week.index, week.start, week.last_day, week.has_extra_week)
```

*Output*

```txt
week 2023 53 2024-01-28 2024-02-03 True
```

**JavaScript**

*parse.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const week = cal.parse("FY2023 W53");
console.log(week.level, week.year, week.index, week.start, week.last_day, week.has_extra_week);
```

*Output*

```txt
week 2023 53 2024-01-28 2024-02-03 true
```

**Rust**

*parse.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let week = cal.parse("FY2023 W53")?;
    println!(
        "{} {} {:?} {} {} {}",
        week.level.name(), week.year, week.index, week.start, week.last_day(), week.has_extra_week
    );

    Ok(())
}
```

*Output*

```txt
week 2023 Some(53) 2024-01-28 2024-02-03 true
```

## dimension

Returns one row per day from `start` up to but not including `end_exclusive`, at most 146,097 days. Python returns a list of dicts and JavaScript an array of objects, with dates as ISO strings; Rust returns `DimensionRow` values with a `location` and the `labels`, in the order of `label_columns()`. The columns are listed in [Date dimension](https://fiscalclock.karthik.nz/guides/date-dimension/#columns).

**Python**

*dimension.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

for row in cal.dimension("2024-02-02", "2024-02-05"):
    print(row["date"], row["fiscal_year"], row["quarter"], row["period"], row["week"], row["label_nrf_week"])
```

*Output*

```txt
2024-02-02 2023 4 12 53 FY2023 W53
2024-02-03 2023 4 12 53 FY2023 W53
2024-02-04 2024 1 1 1 FY2024 W01
```

**JavaScript**

*dimension.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

for (const row of cal.dimension("2024-02-02", "2024-02-05")) {
  console.log(row.date, row.fiscal_year, row.quarter, row.period, row.week, row.label_nrf_week);
}
```

*Output*

```txt
2024-02-02 2023 4 12 53 FY2023 W53
2024-02-03 2023 4 12 53 FY2023 W53
2024-02-04 2024 1 1 1 FY2024 W01
```

**Rust**

*dimension.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let week_label = cal.label_columns().iter().position(|c| c == "label_nrf_week").unwrap();
    for row in cal.dimension("2024-02-02".parse()?, "2024-02-05".parse()?)? {
        let at = row.location;
        println!(
            "{} {} {:?} {:?} {:?} {}",
            at.date,
            at.year.year,
            at.quarter.index,
            at.period.index,
            at.week.index,
            row.labels[week_label].as_deref().unwrap_or("")
        );
    }

    Ok(())
}
```

*Output*

```txt
2024-02-02 2023 Some(4) Some(12) Some(53) FY2023 W53
2024-02-03 2023 Some(4) Some(12) Some(53) FY2023 W53
2024-02-04 2024 Some(1) Some(1) Some(1) FY2024 W01
```

## dimension_csv

Returns the same rows as RFC 4180 CSV text, with a header row.

**Python**

*dimension_csv.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

print(cal.dimension_csv("2024-02-03", "2024-02-05"), end="")
```

*Output*

```txt
date,calendar_id,fiscal_year,quarter,period,week,day_of_year,day_of_quarter,day_of_period,day_of_week,year_start,year_last_day,quarter_start,quarter_last_day,period_start,period_last_day,week_start,week_last_day,year_has_extra_week,period_has_extra_week,week_is_extra_week,label_nrf_year,label_nrf_quarter,label_nrf_period,label_nrf_week
2024-02-03,efc8868a4b19eaab,2023,4,12,53,371,98,35,7,2023-01-29,2024-02-03,2023-10-29,2024-02-03,2023-12-31,2024-02-03,2024-01-28,2024-02-03,true,true,true,FY2023,FY2023 Q4,FY2023 P12,FY2023 W53
2024-02-04,efc8868a4b19eaab,2024,1,1,1,1,1,1,1,2024-02-04,2025-02-01,2024-02-04,2024-05-04,2024-02-04,2024-03-02,2024-02-04,2024-02-10,false,false,false,FY2024,FY2024 Q1,FY2024 P01,FY2024 W01
```

**JavaScript**

*dimension_csv.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

process.stdout.write(cal.dimensionCsv("2024-02-03", "2024-02-05"));
```

*Output*

```txt
date,calendar_id,fiscal_year,quarter,period,week,day_of_year,day_of_quarter,day_of_period,day_of_week,year_start,year_last_day,quarter_start,quarter_last_day,period_start,period_last_day,week_start,week_last_day,year_has_extra_week,period_has_extra_week,week_is_extra_week,label_nrf_year,label_nrf_quarter,label_nrf_period,label_nrf_week
2024-02-03,efc8868a4b19eaab,2023,4,12,53,371,98,35,7,2023-01-29,2024-02-03,2023-10-29,2024-02-03,2023-12-31,2024-02-03,2024-01-28,2024-02-03,true,true,true,FY2023,FY2023 Q4,FY2023 P12,FY2023 W53
2024-02-04,efc8868a4b19eaab,2024,1,1,1,1,1,1,1,2024-02-04,2025-02-01,2024-02-04,2024-05-04,2024-02-04,2024-03-02,2024-02-04,2024-02-10,false,false,false,FY2024,FY2024 Q1,FY2024 P01,FY2024 W01
```

**Rust**

*dimension_csv.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    print!("{}", cal.dimension_csv("2024-02-03".parse()?, "2024-02-05".parse()?)?);

    Ok(())
}
```

*Output*

```txt
date,calendar_id,fiscal_year,quarter,period,week,day_of_year,day_of_quarter,day_of_period,day_of_week,year_start,year_last_day,quarter_start,quarter_last_day,period_start,period_last_day,week_start,week_last_day,year_has_extra_week,period_has_extra_week,week_is_extra_week,label_nrf_year,label_nrf_quarter,label_nrf_period,label_nrf_week
2024-02-03,efc8868a4b19eaab,2023,4,12,53,371,98,35,7,2023-01-29,2024-02-03,2023-10-29,2024-02-03,2023-12-31,2024-02-03,2024-01-28,2024-02-03,true,true,true,FY2023,FY2023 Q4,FY2023 P12,FY2023 W53
2024-02-04,efc8868a4b19eaab,2024,1,1,1,1,1,1,1,2024-02-04,2025-02-01,2024-02-04,2024-05-04,2024-02-04,2024-03-02,2024-02-04,2024-02-10,false,false,false,FY2024,FY2024 Q1,FY2024 P01,FY2024 W01
```

## explain

Returns the rules the definition applies to a date, one step per level, from the year down to the week. The `rule` and `result` text is English for people to read; its wording is not part of the API and may change in any release ([API spec](https://fiscalclock.karthik.nz/reference/spec/api-v1/#operations)).

**Python**

*explain.py*

```python
from pathlib import Path

from fiscalclock import FiscalCalendar

cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))

explanation = cal.explain("2024-02-03")
for step in explanation["steps"]:
    print(f"{step['level']}: {step['result']}")
```

*Output*

```txt
year: fiscal year 2023 runs 2023-01-29 to 2024-02-03 (371 days, 53 weeks, including the extra week)
quarter: quarter 4 runs 2023-10-29 to 2024-02-03 (98 days, 14 weeks, including the extra week)
period: period 12 runs 2023-12-31 to 2024-02-03 (35 days, 5 weeks, including the extra week)
week: week 53 runs 2024-01-28 to 2024-02-03 (7 days, 1 weeks, the extra week); 2024-02-03 is day 7 of the week
```

**JavaScript**

*explain.mjs*

```js
import { readFileSync } from "node:fs";
import { FiscalCalendar } from "fiscalclock";

const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));

const explanation = cal.explain("2024-02-03");
for (const step of explanation.steps) {
  console.log(`${step.level}: ${step.result}`);
}
```

*Output*

```txt
year: fiscal year 2023 runs 2023-01-29 to 2024-02-03 (371 days, 53 weeks, including the extra week)
quarter: quarter 4 runs 2023-10-29 to 2024-02-03 (98 days, 14 weeks, including the extra week)
period: period 12 runs 2023-12-31 to 2024-02-03 (35 days, 5 weeks, including the extra week)
week: week 53 runs 2024-01-28 to 2024-02-03 (7 days, 1 weeks, the extra week); 2024-02-03 is day 7 of the week
```

**Rust**

*explain.rs*

```rust
use std::error::Error;
use std::fs;

use fiscalclock::{FiscalCalendar};

fn main() -> Result<(), Box<dyn Error>> {
    let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;

    let explanation = cal.explain("2024-02-03".parse()?)?;
    for step in explanation.steps {
        println!("{}: {}", step.level.name(), step.result);
    }

    Ok(())
}
```

*Output*

```txt
year: fiscal year 2023 runs 2023-01-29 to 2024-02-03 (371 days, 53 weeks, including the extra week)
quarter: quarter 4 runs 2023-10-29 to 2024-02-03 (98 days, 14 weeks, including the extra week)
period: period 12 runs 2023-12-31 to 2024-02-03 (35 days, 5 weeks, including the extra week)
week: week 53 runs 2024-01-28 to 2024-02-03 (7 days, 1 weeks, the extra week); 2024-02-03 is day 7 of the week
```
