Skip to content

Write a definition

View as Markdown

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 is the full reference; this page is a map of it.

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
week yes The weekday each week starts on, and how week 1 is chosen. 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
naming yes How fiscal years are numbered, and the label templates, such as FY{yyyy} Q{q}. naming
exceptions no Reserved. Any value is rejected with UNSUPPORTED_FEATURE in 0.1.0. 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 has a complete example definition.

No defaults for your organisation’s choices

Section titled “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). Costco calls the fiscal year that ended on 3 Sep 2017 “2017”, which is numbering by the end year (cited vector).
  • extra_week, which says which period gains the 53rd week of a long year. Costco puts it in the thirteenth period (cited vector), and Apple’s first quarter of fiscal 2023 had 14 weeks (cited vector).

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
{
"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}" }
}
}
}
}
definition_errors.py
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
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, 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.

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). The validate operation returns it, and each row of the date dimension carries it, so you can tell which definition produced a stored result.