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 Thursdayday_of_week(2026-09-16)→ 3 an ordinary Wednesdayday_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
| iso | date | ISO date, YYYY-MM-DD |
| returns | int | 1 = 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(…)
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
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.
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Path | Bytes |
|---|---|
| README.md | 995 |
| impl/python.py | 1,264 |
| impl/rust.rs | 1,493 |
| impl/typescript.ts | 1,293 |
| vectors.json | 1,977 |