Functional Weave
Code in Python

dates.week-number

ISO 8601 week number and week-numbering year of a date: weeks start Monday, week 1 holds the first Thursday.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 18 tests, run in TypeScript, Python and Rust.

What it does

The ISO 8601 week of a date. Weeks run Monday to Sunday, and week 1 of a year is the week containing that year's first Thursday (equivalently, the week containing 4 January). A year has 52 or 53 weeks.

The trap is the year. Around 1 January a date can belong to the previous or the next ISO week-year: 1 January 2027 is in week 53 of 2026, and 30 December 2024 is in week 1 of 2025. So the result carries `weekYear` alongside `week`, and `label` ("2026-W53") is built from the week-year, never the calendar year. A report keyed on calendar year and ISO week puts 30 December 2024 in "2024 week 1", eleven months early.

For example

  • week_number(2026-09-23) → week year 2,026, week 39, label 2026-W39 an ordinary Wednesday in September
  • week_number(2026-01-01) → week year 2,026, week 1, label 2026-W01 1 January 2026 is a Thursday, so it is in week 1
  • week_number(2027-01-01) → week year 2,026, week 53, label 2026-W53 1 January 2027 is a Friday and belongs to week 53 of 2026, not week 1 of 2027

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

def week_number(iso: str) -> IsoWeek
isodateany date
returnsIsoWeek

The type it declares, generated into your project

@dataclass(frozen=True)
class IsoWeek:
    """A date's ISO 8601 week."""

    #: the ISO week-numbering year; differs from the calendar year for a few days around 1 January
    week_year: int
    #: 1 to 52 or 53
    week: int
    #: ISO week form, e.g. 2026-W39
    label: str

Your code names it in one line, in the file that uses it

from fune.dates.week_number import week_number  # dates.week-number@^1
impl/python.py · 16 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from .dates_add_days import civil_from_days, days_from_civil, epoch_day_from_iso  ← from dates.add-days ^1.0.0 · built alongside by fune
from .dates_day_of_week import day_of_week  ← from dates.day-of-week ^1.0.0 · built alongside by fune
from .dates_week_number_types import IsoWeek


def week_number(iso: str) -> IsoWeek:
    """The ISO 8601 week of a date.

    The Thursday of the date's Monday-to-Sunday week decides everything: its
    calendar year is the week-year, and its day of that year gives the week
    number.
    """
    thursday = epoch_day_from_iso(iso) + (4 - day_of_week(iso))
    week_year = civil_from_days(thursday).year
    week = (thursday - days_from_civil(week_year, 1, 1)) // 7 + 1
    return IsoWeek(week_year=week_year, week=week, label="%04d-W%02d" % (week_year, week))

Install

fune build

With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add dates.week-number
Download for Python dates.week-number-1.0.0-python.fune · 6,528 bytes sha256 881eccca5bd5e5ecacedff59cc7bbefb13c16197922927ecba323de15f4dea99

The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./dates.week-number-1.0.0-python.fune, or fetch it from a terminal with fune pull dates.week-number@1.0.0:python.

The whole function, every language, is one file too: dates.week-number-1.0.0.fune, 8,545 bytes, sha256 57419625d710371dddc8e6db21a61c341ada81dd0670f975210cf019b72de781. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

# fune: before dates.week-number

after — your function gets the result and the arguments, and returns the final result.

# fune: after dates.week-number

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

# fune: replace dates.add-days in dates.week-number
# fune: replace dates.day-of-week in dates.week-number

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show dates.week-number --steps.

# fune: step dates.week-number after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
an ordinary Wednesday in September 2026-09-23 → week year 2,026, week 39, label 2026-W39
1 January 2026 is a Thursday, so it is in week 1 2026-01-01 → week year 2,026, week 1, label 2026-W01
1 January 2027 is a Friday and belongs to week 53 of 2026, not week 1 of 2027 2027-01-01 → week year 2,026, week 53, label 2026-W53
Sunday 3 January 2027 still ends week 53 of 2026 2027-01-03 → week year 2,026, week 53, label 2026-W53
Monday 4 January 2027 starts week 1 of 2027 2027-01-04 → week year 2,027, week 1, label 2027-W01
30 December 2024 is in week 1 of 2025: calendar year and week-year differ 2024-12-30 → week year 2,025, week 1, label 2025-W01
29 December 2008 is in week 1 of 2009 2008-12-29 → week year 2,009, week 1, label 2009-W01
2020 had 53 weeks: 31 December 2020 is in week 53 2020-12-31 → week year 2,020, week 53, label 2020-W53
3 January 2021 is still in week 53 of 2020 2021-01-03 → week year 2,020, week 53, label 2020-W53
2015 had 53 weeks 2015-12-31 → week year 2,015, week 53, label 2015-W53
Show the other 8 tests
CaseArgumentsExpected
31 December 2026 is in week 53 of 2026 2026-12-31 → week year 2,026, week 53, label 2026-W53
2004 was a leap year starting on Thursday, so it had 53 weeks 2004-12-31 → week year 2,004, week 53, label 2004-W53
3 January 2010 belongs to week 53 of 2009 2010-01-03 → week year 2,009, week 53, label 2009-W53
1 January 1900 was a Monday: week 1 1900-01-01 → week year 1,900, week 1, label 1900-W01
the first supported date was a Monday: 0001-W01 0001-01-01 → week year 1, week 1, label 0001-W01
the last supported date is in week 52 of 9999 9999-12-31 → week year 9,999, week 52, label 9999-W52
an impossible date is an error 2026-02-29 → error: is not a real calendar date
an ISO week string is not a date 2026-W39 → error: is not an ISO date

More from the author

This is the ISO scheme only. US-style week numbers (weeks starting Sunday, week 1 containing 1 January) and "week of the month" are different questions.

The week is found from the Thursday of the date's week, whose calendar year is by definition the week-year, using the dates.add-days kernel and dates.day-of-week, so it is exact for every date from 0001-01-01 to 9999-12-31.

Files

PathBytes
README.md1,020
impl/python.py687
impl/rust.rs1,149
impl/typescript.ts768
vectors.json2,600