Skip to content

API reference

View as Markdown

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; the Rust crate’s full API is on docs.rs.

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, and prints exactly the output shown under it.

Operation Python JavaScript Rust
Build a calendar FiscalCalendar new FiscalCalendar FiscalCalendar::from_json
validate validate validate FiscalCalendar::from_json
locate locate locate locate
period period period period
shift shift shift shift
parent parent parent parent
children children children children
years years years years
format format format format
parse parse parse parse
dimension dimension dimension dimension
dimension_csv dimension_csv dimensionCsv dimension_csv
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). Dates follow Dates and intervals.

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).

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).

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.

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).

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 shows an invalid definition.

validate.py
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
True efc8868a4b19eaab

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

locate.py
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
2023 4 12 53
371 98 35 7

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

period.py
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
2023-10-29 2024-02-04 2024-02-03 98 14 True

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

shift.py
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
1 FY2024 Q1 2024-02-04
-4 FY2022 Q4 2022-10-30

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.

parent.py
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
FY2023 W53 -> FY2023 P12 -> FY2023 Q4 -> FY2023

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.

children.py
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
FY2023 P10 4 False
FY2023 P11 5 False
FY2023 P12 5 True

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

years.py
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
2021 2021-01-31 52
2022 2022-01-30 52
2023 2023-01-29 53
2024 2024-02-04 52

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.

format.py
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
FY2023 W53
FY2023 P12

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).

parse.py
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
week 2023 53 2024-01-28 2024-02-03 True

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.

dimension.py
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
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

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

dimension_csv.py
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
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

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).

explain.py
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
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