Skip to content

Compute fiscal calendars from a JSON definition

Describe your organisation’s fiscal calendar as a JSON definition, and fiscalclock tells you which fiscal year, quarter, period and week any date falls in, the bounds of every period, and the labels you use for them. One Rust core gives the same answers in Python, JavaScript and Rust.

pip install fiscalclock

Get startedTry it in the playground

An illustrative 4-5-4 definition, not any organisation’s published calendar. Its year rule gives the 53-week years NRF publishes. fiscalclock has no built-in calendars.

retail-4-5-4.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], "extra_week": { "period": 12 } },
"naming": { "default": "nrf",
"schemes": { "nrf": { "year_number": "start",
"templates": { "year": "FY{yyyy}", "quarter": "FY{yyyy} Q{q}",
"period": "FY{yyyy} P{pp}", "week": "FY{yyyy} W{ww}" } } } }
}
FY2023: 371 days, 53 weeksYear [2023-01-29, 2024-02-04). Q1 [2023-01-29, 2023-04-30), 13 weeks. Q2 [2023-04-30, 2023-07-30), 13 weeks. Q3 [2023-07-30, 2023-10-29), 13 weeks. Q4 [2023-10-29, 2024-02-04), 14 weeks. P1 [2023-01-29, 2023-02-26), 4 weeks. P2 [2023-02-26, 2023-04-02), 5 weeks. P3 [2023-04-02, 2023-04-30), 4 weeks. P4 [2023-04-30, 2023-05-28), 4 weeks. P5 [2023-05-28, 2023-07-02), 5 weeks. P6 [2023-07-02, 2023-07-30), 4 weeks. P7 [2023-07-30, 2023-08-27), 4 weeks. P8 [2023-08-27, 2023-10-01), 5 weeks. P9 [2023-10-01, 2023-10-29), 4 weeks. P10 [2023-10-29, 2023-11-26), 4 weeks. P11 [2023-11-26, 2023-12-31), 5 weeks. P12 [2023-12-31, 2024-02-04), 5 weeks, including the extra week as its last week.P14 wkP25 wkP34 wkP44 wkP55 wkP64 wkP74 wkP85 wkP94 wkP104 wkP115 wkP125 wkQ113 weeksQ213 weeksQ313 weeksQ414 weeksextra week 532023-01-292023-02-262023-04-022023-04-302023-05-282023-07-022023-07-302023-08-272023-10-012023-10-292023-11-262023-12-312024-02-04start, includedend_exclusive, excluded

Fiscal 2023 under that definition, drawn to scale: 53 weeks from 29 Jan 2023 up to, but not including, 4 Feb 2024. Each period is a half-open interval, so the date under a boundary line is the first day of the period to its right. The extra 53rd week is the last week of period 12. The dates were computed by the fiscalclock 0.1.0 npm package when this page was built, and the build fails unless the year has the 53 weeks that NRF’s list of 53-week years gives it and the dates match the dates from an independent implementation of the spec.

Every period's dates
PeriodStartEnd (excluded)Last dayWeeks
P14
P25
P34
P44
P55
P64
P74
P85
P94
P104
P115
P12, with the extra week5

Which fiscal quarter is 3 Feb 2024 in, and what is it called? Save the definition above as retail-4-5-4.json and ask:

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(cal.format(here.quarter))
print(cal.call_json("locate", '{"date": "2024-02-03"}'))

All three print the same two lines: the label, then the core’s JSON for the date, byte for byte the same in every language. Each snippet is a file in website/examples/ that npm run test:examples runs against version 0.1.0 of its package, and it must print exactly the output below.

Output
FY2023 Q4
{"date":"2024-02-03","year":{"level":"year","year":2023,"index":null,"start":"2023-01-29","end_exclusive":"2024-02-04","last_day":"2024-02-03","days":371,"weeks":53,"has_extra_week":true},"quarter":{"level":"quarter","year":2023,"index":4,"start":"2023-10-29","end_exclusive":"2024-02-04","last_day":"2024-02-03","days":98,"weeks":14,"has_extra_week":true},"period":{"level":"period","year":2023,"index":12,"start":"2023-12-31","end_exclusive":"2024-02-04","last_day":"2024-02-03","days":35,"weeks":5,"has_extra_week":true},"week":{"level":"week","year":2023,"index":53,"start":"2024-01-28","end_exclusive":"2024-02-04","last_day":"2024-02-03","days":7,"weeks":1,"has_extra_week":true},"day_of_year":371,"day_of_quarter":98,"day_of_period":35,"day_of_week":7}
  • Shared conformance vectors. The vector files in conformance/ are run by the test suite of every package. Expected results come from cited sources or from generators written independently of the library, never from the library’s own output.
  • External oracles. Week-based years are checked against Python’s ISO 8601 week dates (date.isocalendar()) for every year from 1600 to 2399, and the NRF 4-5-4 definition against the 53-week years NRF publishes (2006, 2012, 2017 and 2023).
  • A second implementation. A deliberately slow day-by-day reference implementation is compared with the core for every date from 1900 to 2200 across 960 generated definitions.
  • Property tests. Periods tile their year without gaps or overlaps, nest correctly, and round-trip through shift and through format and parse. Week-based years have 364 or 371 days and 71 long years in every 400.
  • Identical output in every language. On every push, CI runs all vectors through Rust and through the Python and npm packages on Linux, macOS and Windows, and requires the raw outputs to match the Rust output byte for byte.
  • Reproducible verification. Every date-arithmetic claim in the design documents is reproduced by a standard-library script in research/verification/, which CI reruns on every pull request.

These statements are taken from the project README.

All three packages are built from the same Rust core and return the same JSON for the same input. Version 0.1.0 is the first release; the definition format and API are version 1 drafts and may change before 1.0.

Package Install Supports
Python package pip install fiscalclock CPython 3.10 and later, and free-threaded 3.14; wheels for Linux, macOS and Windows
npm package npm install fiscalclock Node.js 22 and later, and browsers (WebAssembly)
Rust crate cargo add fiscalclock Rust 1.85 and later, no_std with alloc