# Write a definition

The five blocks of a calendar definition, what each decides, which fields are required, and why organisation-specific choices have no default.

A definition is one JSON object. Besides `spec_version`, which must be `1`, it has five blocks, and each block decides one thing about the calendar. The [definition format spec](https://fiscalclock.karthik.nz/reference/spec/definition-v1/) is the full reference; this page is a map of it.

## The five blocks

| Block | Required | What it decides | Spec |
| - | - | - | - |
| `year` | yes | Where each fiscal year starts and ends: on a fixed date (`fixed_date`), or after whole weeks by a weekday rule (`week_based`). | [`year`](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#year) |
| `week` | yes | The weekday each week starts on, and how week 1 is chosen. | [`week`](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#week) |
| `subdivision` | yes | How the year splits into periods and quarters: calendar months (`calendar_months`) or a pattern of weeks such as 4-5-4 (`week_pattern`). | [`subdivision`](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#subdivision) |
| `naming` | yes | How fiscal years are numbered, and the label templates, such as `FY{yyyy} Q{q}`. | [`naming`](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#naming) |
| `exceptions` | no | Reserved. Any value is rejected with `UNSUPPORTED_FEATURE` in 0.1.0. | [Reserved for later releases](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#reserved-for-later-releases) |

The blocks are parsed and validated separately, and no field in one block changes the meaning of a field in another, except where the spec says so. For example, a week-based year always ends on the weekday before `week.starts_on`, a `calendar_months` subdivision is only valid in a `fixed_date` year and a `week_pattern` subdivision only in a `week_based` year, and `month_start_day` must equal `year.start.day`.

[The quick start](https://fiscalclock.karthik.nz/start-here/quick-start/) has a complete example definition.

## No defaults for your organisation's choices

Some fields record a choice that organisations make differently, so the format gives them no default and a definition that leaves one out is rejected. Two of them are:

- **`year_number`**, which numbers a fiscal year by the calendar year of its first day (`start`), of its last day (`end`), or of most of its days (`majority`). Kroger calls the fiscal year that ended on 2 Feb 2019 "2018", which is numbering by the start year ([cited vector](https://github.com/Karthik-beta/fiscalclock/blob/main/conformance/vectors/cited/kroger-annual-report-2018.json)). Costco calls the fiscal year that ended on 3 Sep 2017 "2017", which is numbering by the end year ([cited vector](https://github.com/Karthik-beta/fiscalclock/blob/main/conformance/vectors/cited/costco-annual-report-2017.json)).
- **`extra_week`**, which says which period gains the 53rd week of a long year. Costco puts it in the thirteenth period ([cited vector](https://github.com/Karthik-beta/fiscalclock/blob/main/conformance/vectors/cited/costco-annual-report-2017.json)), and Apple's first quarter of fiscal 2023 had 14 weeks ([cited vector](https://github.com/Karthik-beta/fiscalclock/blob/main/conformance/vectors/cited/apple-10q-2023-12-30.json)).

A default would give a wrong answer, without any warning, to every organisation that made the other choice. A missing choice is an error instead, and the error names the field. This illustrative definition leaves out both:

*missing-choices.json*

```json
{
  "spec_version": 1,
  "year": {
    "type": "week_based",
    "anchor": { "edge": "end", "month": 1, "day": "last", "rule": "nearest" }
  },
  "week": { "starts_on": "sunday", "numbering": "contains_start" },
  "subdivision": {
    "type": "week_pattern",
    "periods": [4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4],
    "quarters": [3, 3, 3, 3]
  },
  "naming": {
    "default": "nrf",
    "schemes": {
      "nrf": {
        "templates": { "year": "FY{yyyy}" }
      }
    }
  }
}
```

**Python**

*definition_errors.py*

```python
from pathlib import Path

from fiscalclock import validate

result = validate(Path("missing-choices.json").read_text(encoding="utf-8"))
for error in result["errors"]:
    print(error["path"], error["code"])
```

*Output*

```txt
naming.schemes.nrf.year_number MISSING_FIELD
subdivision.extra_week EXTRA_WEEK_MISSING
```

**JavaScript**

*definition_errors.mjs*

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

const result = validate(readFileSync("missing-choices.json", "utf8"));
for (const error of result.errors) {
  console.log(error.path, error.code);
}
```

*Output*

```txt
naming.schemes.nrf.year_number MISSING_FIELD
subdivision.extra_week EXTRA_WEEK_MISSING
```

**Rust**

*definition_errors.rs*

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

use fiscalclock::FiscalCalendar;

fn main() -> Result<(), Box<dyn Error>> {
    match FiscalCalendar::from_json(&fs::read_to_string("missing-choices.json")?) {
        Ok(_) => println!("valid"),
        Err(errors) => {
            for error in errors.as_slice() {
                println!("{} {}", error.path, error.code);
            }
        }
    }
    Ok(())
}
```

*Output*

```txt
naming.schemes.nrf.year_number MISSING_FIELD
subdivision.extra_week EXTRA_WEEK_MISSING
```

Validation reports every error it finds, sorted by path and then by code. Each error has a stable `code` from the [error catalogue](https://fiscalclock.karthik.nz/reference/spec/api-v1/#error-catalogue), the JSON `path` of the field, a `message` and, where one helps, a `hint`. Match on the code and the path, not on the message, whose wording may change.

## Two definitions, one calendar

A valid definition has a `calendar_id`: the first 16 hexadecimal digits of the SHA-256 digest of its canonical form. Two definitions that differ only in key order, whitespace or written-out defaults have the same `calendar_id` ([spec](https://fiscalclock.karthik.nz/reference/spec/definition-v1/#canonical-form-and-calendar_id)). The [`validate` operation](https://fiscalclock.karthik.nz/reference/api/#validate) returns it, and each row of the [date dimension](https://fiscalclock.karthik.nz/guides/date-dimension/) carries it, so you can tell which definition produced a stored result.
