Functional Weave
Code in Rust

dates.day-of-week

The ISO 8601 day of the week for a date: 1 is Monday through 7 is Sunday.

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

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

What it does

ISO numbering, 1 = Monday to 7 = Sunday. The alternatives are all off by something: JavaScript's getDay() is 0 = Sunday, Python's weekday() is 0 = Monday and isoweekday() is 1 = Monday. Fixing one convention across the three languages is the point, and it makes a weekend test read as day >= 6 everywhere.

It depends on dates.add-days rather than re-deriving days-from-civil: the calendar kernel has one home in the registry, so a fix to it is a fix everywhere.

For example

  • day_of_week(1970-01-01) → 4 the epoch itself, 1 January 1970, was a Thursday
  • day_of_week(2026-09-16) → 3 an ordinary Wednesday
  • day_of_week(2026-09-19) → 6 Saturday is 6

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.

pub fn day_of_week(iso: &str) -> i64
isodateISO date, YYYY-MM-DD
returnsint1 = Monday ... 7 = Sunday

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

fune!(dates.day-of-week@^1);  // then call day_of_week(…)
impl/rust.rs · 48 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.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::dates_add_days::epoch_day_from_iso;  ← from dates.add-days ^1.0.0 · built alongside by fune

pub const DAY_NAMES: [&str; 7] = [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday",
];

/// The ISO 8601 day of the week: 1 = Monday through 7 = Sunday.
///
/// ISO numbering because it is what the rest of the business world means by
/// "day 1", and because a weekend test is then `day >= 6` in every language.
///
/// # Panics
/// Panics if the date is malformed or impossible.
pub fn day_of_week(iso: &str) -> i64 {
    // 1970-01-01, epoch day 0, was a Thursday. Shifting by 3 moves the origin of
    // the cycle to Monday so that the remainder is already ISO numbering.
    // rem_euclid, not %, because dates before 1970 have negative day numbers and
    // Rust's % keeps the sign of the dividend.
    (epoch_day_from_iso(iso) + 3).rem_euclid(7) + 1
}

/// True for Saturday and Sunday.
pub fn is_weekend(iso: &str) -> bool {
    day_of_week(iso) >= 6
}

/// True for Monday to Friday. Says nothing about public holidays.
pub fn is_weekday(iso: &str) -> bool {
    day_of_week(iso) <= 5
}

/// The English name, for logs and labels. Not localised, deliberately: a
/// capability that needed locale data would need a locale argument and a data
/// file to pin it.
pub fn day_name(iso: &str) -> &'static str {
    DAY_NAMES[(day_of_week(iso) - 1) as usize]
}

pub fn fune_vector(args: &[Value]) -> Value {
    Value::Int(day_of_week(args[0].as_str()))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the Rust 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. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add dates.day-of-week
Download for Rust dates.day-of-week-1.0.0-rust.fune · 5,987 bytes sha256 3c4d9373a5933a37ab4aefdf9e3aaccaf7305bf6141193e6604c514995e30f0a

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

The whole function, every language, is one file too: dates.day-of-week-1.0.0.fune, 8,714 bytes, sha256 976f7dbb98c070afdfb65de0f361d7b00c14159b37a0ee2615f23fc535081d4a. 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.day-of-week

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

// fune: after dates.day-of-week

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.day-of-week

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.day-of-week --steps.

// fune: step dates.day-of-week 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
the epoch itself, 1 January 1970, was a Thursday 1970-01-01 → 4
an ordinary Wednesday 2026-09-16 → 3
Saturday is 6 2026-09-19 → 6
Sunday is 7, not 0 2026-09-20 → 7
Monday is 1 2026-09-21 → 1
1 January 2000 was a Saturday 2000-01-01 → 6
31 December 1999 was a Friday 1999-12-31 → 5
1 January 1900 was a Monday, well before the epoch 1900-01-01 → 1
the 2024 leap day was a Thursday 2024-02-29 → 4
1 March 1900 was a Thursday because 1900 had no leap day 1900-03-01 → 4
Show the other 10 tests
CaseArgumentsExpected
1 March 2000 was a Wednesday because 2000 did have one 2000-03-01 → 3
the first supported date, 1 January 0001, is a Monday 0001-01-01 → 1
the last supported date, 31 December 9999, is a Friday 9999-12-31 → 5
a slash-separated date is an error, not a guess 16/09/2026 → error: is not an ISO date
an unpadded month is an error 2026-9-16 → error: is not an ISO date
an empty string is an error → error: is not an ISO date
30 February is not a real date 2026-02-30 → error: is not a real calendar date
29 February in a non-leap year is rejected 2026-02-29 → error: is not a real calendar date
month 00 is rejected 2026-00-10 → error: is not a real calendar date
day 00 is rejected 2026-09-00 → error: is not a real calendar date

More from the author

The day number comes from the epoch day, not from a table, so it is correct for dates before 1970 (where the day number is negative) as well as after. Getting the sign right matters: a plain remainder in Rust or TypeScript keeps the sign of the dividend and would return a negative weekday for 1900 dates, so both use a floored remainder.

It says nothing about public holidays. Working days are dates.business-days-between, which takes the holiday list as an argument because no library knows your company's.

Files

PathBytes
README.md995
impl/python.py1,264
impl/rust.rs1,493
impl/typescript.ts1,293
vectors.json1,977