Write a definition
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.
The five blocks
Section titled “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 |
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:
{ "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}" } } } }}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"])naming.schemes.nrf.year_number MISSING_FIELDsubdivision.extra_week EXTRA_WEEK_MISSINGimport { 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);}naming.schemes.nrf.year_number MISSING_FIELDsubdivision.extra_week EXTRA_WEEK_MISSINGuse 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(())}naming.schemes.nrf.year_number MISSING_FIELDsubdivision.extra_week EXTRA_WEEK_MISSINGValidation 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.
Two definitions, one calendar
Section titled “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). The validate operation returns it, and each row of the date dimension carries it, so you can tell which definition produced a stored result.