todo.due-label
The short English label shown next to a todo: "2 days overdue", "Due today", "Due tomorrow", "Due Fri 2 Oct".
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 21 tests, run in TypeScript, Python and Rust.
What it does
The short label an app shows next to a todo, worked out from todo.due-status so a todo labelled overdue is also in the Overdue section and counted as overdue in the summary.
| Status | Label | | --- | --- | | done, or no due date | `""` (show nothing) | | overdue | `"1 day overdue"`, `"12 days overdue"` | | today | `"Due today"` | | tomorrow | `"Due tomorrow"` | | this week or later | `"Due Fri 2 Oct"`; `"Due Fri 1 Jan 2027"` when the year differs from today's |
For example
dueLabel(id a, title Task a, notes —, done true, priority none, due 2026-09-20, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28)→ a done todo shows nothing, even when its date has passeddueLabel(id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28)→ a done todo with no date shows nothingdueLabel(id a, title Task a, notes —, done false, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28)→ no due date shows nothing
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.
export function dueLabel(todo: Todo, today: string): string
| todo | Todo | |
| today | date | the user's local date, from the app; never the clock |
| returns | string | empty for a done todo or one with no due date |
Your code names it in one line, in the file that uses it
import { dueLabel } from "#fune/todo.due-label@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { parseIsoDate } from "./dates_add_days.ts"; ← from dates.add-days ^1.0.0 · built alongside by fune
import { dayName } from "./dates_day_of_week.ts"; ← from dates.day-of-week ^1.0.0 · built alongside by fune
import { daysBetween } from "./dates_days_between.ts"; ← from dates.days-between ^1.0.0 · built alongside by fune
import { dueStatus } from "./todo_due_status.ts"; ← from todo.due-status ^1.0.0 · built alongside by fune
import { type Todo } from "./todo_item.ts"; ← from todo.item ^1.0.0 · built alongside by fune
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
/**
* The short English label for a todo's due date. Built on dueStatus so the
* label, the section and the overdue count never disagree. A year is added
* only when the due date is in a different calendar year from today.
*/
export function dueLabel(todo: Todo, today: string): string {
const status = dueStatus(todo, today);
if (status === "done" || status === "none") return "";
const due = todo.due as string;
if (status === "overdue") {
const late = daysBetween(due, today);
return late === 1 ? "1 day overdue" : `${late} days overdue`;
}
if (status === "today") return "Due today";
if (status === "tomorrow") return "Due tomorrow";
const d = parseIsoDate(due);
const label = `Due ${dayName(due).slice(0, 3)} ${d.day} ${MONTHS[d.month - 1]}`;
return d.year === parseIsoDate(today).year ? label : `${label} ${d.year}`;
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 5 dependencies, pins them in fune.lock, downloads only the TypeScript 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 todo.due-label
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./todo.due-label-1.0.0-typescript.fune, or fetch it from a terminal with fune pull todo.due-label@1.0.0:typescript.
The whole function, every language, is one file too: todo.due-label-1.0.0.fune, 14,494 bytes, sha256 8aefefe87feab04119b7d82cf60a2bc4f421a0265722e14f34271dd4a0d7e21e. 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 todo.due-label
after — your function gets the result and the arguments, and returns the final result.
// fune: after todo.due-label
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 todo.due-label
// fune: replace dates.day-of-week in todo.due-label
// fune: replace dates.days-between in todo.due-label
// fune: replace todo.due-status in todo.due-label
// fune: replace todo.item in todo.due-label
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 todo.due-label --steps.
// fune: step todo.due-label 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 | |
|---|---|---|---|
| a done todo shows nothing, even when its date has passed | id a, title Task a, notes —, done true, priority none, due 2026-09-20, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28 | → | |
| a done todo with no date shows nothing | id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-09-28 | → | |
| no due date shows nothing | id a, title Task a, notes —, done false, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | |
| one day late is singular | id a, title Task a, notes —, done false, priority none, due 2026-09-27, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | 1 day overdue |
| ten days late | id a, title Task a, notes —, done false, priority none, due 2026-09-18, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | 10 days overdue |
| due last New Year's Eve is 271 days overdue | id a, title Task a, notes —, done false, priority none, due 2025-12-31, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | 271 days overdue |
| due today | id a, title Task a, notes —, done false, priority none, due 2026-09-28, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due today |
| due tomorrow | id a, title Task a, notes —, done false, priority none, due 2026-09-29, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due tomorrow |
| Friday this week, seen on Monday | id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due Fri 2 Oct |
| Sunday is still this week | id a, title Task a, notes —, done false, priority none, due 2026-10-04, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due Sun 4 Oct |
Show the other 11 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| next Monday is later, same format | id a, title Task a, notes —, done false, priority none, due 2026-10-05, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due Mon 5 Oct |
| the day has no leading zero | id a, title Task a, notes —, done false, priority none, due 2026-11-09, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due Mon 9 Nov |
| a date next year carries the year | id a, title Task a, notes —, done false, priority none, due 2027-01-01, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | Due Fri 1 Jan 2027 |
| on Thursday 31 December, Sunday 3 January is this week and carries the year | id a, title Task a, notes —, done false, priority none, due 2027-01-03, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-12-31 | → | Due Sun 3 Jan 2027 |
| on New Year's Day, New Year's Eve is 1 day overdue | id a, title Task a, notes —, done false, priority none, due 2026-12-31, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2027-01-01 | → | 1 day overdue |
| on Saturday, Sunday is tomorrow | id a, title Task a, notes —, done false, priority none, due 2026-10-04, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-10-03 | → | Due tomorrow |
| on Saturday, Monday is later | id a, title Task a, notes —, done false, priority none, due 2026-10-05, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-10-03 | → | Due Mon 5 Oct |
| today in the wrong format | id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 28/09/2026 | → | error: is not an ISO date |
| today with a trailing newline | id a, title Task a, notes —, done false, priority none, due 2026-10-02, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | error: is not an ISO date |
| a due date that never existed | id a, title Task a, notes —, done false, priority none, due 2026-02-30, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at —, order 0, 2026-09-28 | → | error: is not a real calendar date |
| a bad today is an error even on a done todo | id a, title Task a, notes —, done true, priority none, due —, tags , recurrence —, created at 2026-09-01T09:00:00Z, completed at 2026-09-27T18:00:00Z, order 0, 2026-9-28 | → | error: is not an ISO date |
More from the author
- **English only.** Three-letter English weekday and month names, the day without a leading zero. A localised app should not translate these strings: map the `DueStatus` from todo.due-status itself and format the date with its own locale data. - The overdue count is whole calendar days (dates.days-between), since due dates have no time of day. There is no "yesterday": one day late is `"1 day overdue"`. - The year is added whenever the due date is in a different calendar year from today, in either direction, so a date next January is never mistaken for this January. - `today` is passed in by the app. Malformed or impossible dates are errors, with dates.add-days's messages, even on a done todo.
Files
| Path | Bytes |
|---|---|
| README.md | 1,205 |
| impl/python.py | 1,089 |
| impl/rust.rs | 1,741 |
| impl/typescript.ts | 1,211 |
| vectors.json | 6,442 |