Week-based years
A week-based fiscal year is made of whole weeks, so it cannot always end on the same date. Instead it ends on a weekday chosen by a rule, such as “the Saturday nearest 31 January”. Such a year has 52 weeks (364 days) or 53 weeks (371 days); the 53rd week is called the extra week.
The anchor rule
Section titled “The anchor rule”A week_based year has an anchor with four fields (spec):
edge: whether the rule fixes the year’s last day (end) or its first day (start).monthandday: the target date, wheredayis 1 to 28 or"last"for the month’s last day.rule:lasttakes the latest qualifying weekday on or before the target date,firstthe earliest on or after it, andnearestthe one closest to it.nearestcan never tie, because the closest qualifying weekday is at most three days away.
The anchor has no weekday field. The year’s last day is always the weekday before week.starts_on, so a calendar whose weeks start on Sunday has years that end on a Saturday.
The two rules for 52- and 53-week tax years that IRS Publication 538 describes, a weekday that last occurs in a month or the one nearest the month’s last day, are rule: "last" and rule: "nearest" with day: "last". Apple’s fiscal year, for example, ends on the last Saturday of September (cited vector).
Which years have 53 weeks
Section titled “Which years have 53 weeks”Every week-based rule gives 53-week years, 71 in every 400 years (spec). Check C01 of verify_claims.py confirms the count for all 168 last and nearest rules over two 400-year windows. Which years have 53 weeks depends on the rule.
The example definition from the quick start ends each year on the Saturday nearest 31 January. Its 53-week years from 2006 to 2023 are 2006, 2012, 2017 and 2023, which are the years that the NRF lists as 53-week years for its 4-5-4 calendar. The conformance vector nrf-53-week-years.json checks the same years on every test run.
from pathlib import Path
from fiscalclock import FiscalCalendar
cal = FiscalCalendar(Path("retail-4-5-4.json").read_text(encoding="utf-8"))
long_years = [year.year for year in cal.years(2006, 2023) if year.weeks == 53]print(*long_years)
extra = cal.period("week", 2023, 53)print(extra.start, extra.last_day, extra.has_extra_week)
for year in (2022, 2023): print(year, cal.period("period", year, 12).weeks)2006 2012 2017 20232024-01-28 2024-02-03 True2022 42023 5import { readFileSync } from "node:fs";import { FiscalCalendar } from "fiscalclock";
const cal = new FiscalCalendar(readFileSync("retail-4-5-4.json", "utf8"));
const longYears = cal.years(2006, 2023).filter((year) => year.weeks === 53).map((year) => year.year);console.log(...longYears);
const extra = cal.period("week", 2023, 53);console.log(extra.start, extra.last_day, extra.has_extra_week);
for (const year of [2022, 2023]) { console.log(year, cal.period("period", year, 12).weeks);}2006 2012 2017 20232024-01-28 2024-02-03 true2022 42023 5use std::error::Error;use std::fs;
use fiscalclock::{FiscalCalendar, Level};
fn main() -> Result<(), Box<dyn Error>> { let cal = FiscalCalendar::from_json(&fs::read_to_string("retail-4-5-4.json")?)?;
let long_years: Vec<String> = cal .years(2006, 2023)? .iter() .filter(|year| year.weeks == Some(53)) .map(|year| year.year.to_string()) .collect(); println!("{}", long_years.join(" "));
let extra = cal.period(Level::Week, 2023, Some(53))?; println!("{} {} {}", extra.start, extra.last_day(), extra.has_extra_week);
for year in [2022, 2023] { println!("{} {:?}", year, cal.period(Level::Period, year, Some(12))?.weeks); }
Ok(())}2006 2012 2017 20232024-01-28 2024-02-03 true2022 Some(4)2023 Some(5)Where the extra week goes
Section titled “Where the extra week goes”subdivision.extra_week.period names the period that gains the extra week as its last week. It is required, because organisations put it in different places; Write a definition gives examples. In the example definition it is period 12, so in fiscal year 2023 period 12 has 5 weeks instead of 4, and week 53 runs from 28 Jan to 3 Feb 2024, as the output above shows.
Every period that contains the extra week reports has_extra_week: the year, its quarter and its period are true, and so is the week itself.
Numbering week-based years
Section titled “Numbering week-based years”When the year boundary can fall on either side of 1 January, numbering years by their start or end calendar year can give two fiscal years the same number. Validation checks every fiscal year in the supported range and rejects such a scheme with YEAR_NUMBER_COLLISION, with a hint that names a year_number that works (spec). ISO 8601 week years, for example, must use majority.