Functional Weave
Code in Python

Suites · todo

Todo and task lists

A to-do list's logic as pure functions: the item type, a list reducer, quick-add parsing, due dates, repeats, tags, filters, sorting and CSV import/export.

41 capabilities (16 core, 25 optional) and 8 gaps, in build order. Put each line in the file that calls it, then fune build.

1. Items and the list

The todo type and every change to a list as a pure reducer.

todo.item core
The Todo, TodoDraft and Recurrence types, with validators giving a message per field.
The to-do item type (Todo, TodoDraft, Recurrence) and its validators, with a message per field.
from fune.todo.item import validate_draft, validate_todo  # todo.item@^1
todo.list core
Adds, edits, completes, removes, clears and reorders todos, each returning a new list.
The to-do list reducer: add, edit, complete, remove, clear and reorder todos, each returning a new list.
from fune.todo.list import add_todo, update_todo, toggle_todo, remove_todo, clear_completed, toggle_all, move_todo, toggle_all_mode  # todo.list@^1

2. Quick entry

Turn what people type into todos.

todo.parse-quick-add core
Turns "Pay rent tomorrow !high
Turn one typed line like "Pay rent tomorrow !high #home every month" into a todo draft.
from fune.todo.parse_quick_add import parse_quick_add  # todo.parse-quick-add@^1
todo.parse-date-phrase optional
Reads "tomorrow", "next friday", "in 2 weeks" as a date relative to today.
Read a typed due date like "tomorrow", "next friday", "in 2 weeks" or "2026-10-01" as a date, relative to today.
from fune.todo.parse_date_phrase import parse_date_phrase  # todo.parse-date-phrase@^1
todo.parse-tags optional
Reads a tags box ("#Home, work") into normalised tags.
Read a tags box as typed, "#Home, work errands", into normalised tags: split on spaces and commas.
from fune.todo.parse_tags import parse_tags  # todo.parse-tags@^1
todo.normalise-tags optional
Cleans tags: trims, drops
Clean up tags as typed: trim, drop the #, lower-case slug form, drop empties and duplicates, keep the order.
from fune.todo.normalise_tags import normalise_tags  # todo.normalise-tags@^1
todo.parse-repeat optional
Reads a Repeat box such as "every 2 weeks" as a recurrence.
Read a Repeat box, "every 2 weeks" or "every monday on 2026-10-05", as a todo's recurrence, or null.
from fune.todo.parse_repeat import parse_repeat  # todo.parse-repeat@^1

3. Due dates and repeats

Say where each todo stands against its date, and roll repeating ones forward.

todo.due-status core
Done, overdue, today, tomorrow, this week, later or no date, for a todo today.
Where a todo stands against its due date today: done, overdue, today, tomorrow, this week, later or no date.
from fune.todo.due_status import due_status  # todo.due-status@^1
todo.due-label core
The short label beside a todo: "2 days overdue", "Due tomorrow".
The short English label shown next to a todo: "2 days overdue", "Due today", "Due tomorrow", "Due Fri 2 Oct".
from fune.todo.due_label import due_label  # todo.due-label@^1
todo.next-occurrence optional
The next due date of a repeating todo once done, month-end safe.
The next due date of a repeating todo once it is done: daily, weekdays, weekly, monthly or yearly, month-end safe.
from fune.todo.next_occurrence import next_occurrence  # todo.next-occurrence@^1
todo.reschedule-recurrence optional
Moves a repeating todo's series when its due date is changed by hand.
When a repeating todo's due date is moved by hand, move its series with it: the new date becomes the anchor.
from fune.todo.reschedule_recurrence import reschedule_recurrence  # todo.reschedule-recurrence@^1
todo.recurrence-label optional
Text for a repeat chip: "Every weekday", "Every month on the 31st".
English text for a repeating todo's chip: "Every weekday", "Every 2 weeks on Monday", "Every month on the 31st".
from fune.todo.recurrence_label import recurrence_label  # todo.recurrence-label@^1

4. Views

The list as people see it: filtered, sorted, grouped and counted.

todo.filter core
All, active or completed, one tag and a search box.
The todos a list view shows: all, active or completed, optionally one tag, and a search box, in input order.
from fune.todo.filter import filter_todos  # todo.filter@^1
todo.sort core
Sorts by manual order, due date, priority, creation time or title, stably.
Sort todos by manual order, due date, priority, creation time or title, stably, with sensible tie-breakers.
from fune.todo.sort import sort_todos  # todo.sort@^1
todo.group-by-due optional
Overdue, Today, Tomorrow, This week, Later, No date and Done sections.
Split todos into Overdue, Today, Tomorrow, This week, Later, No date and Done sections, in that order.
from fune.todo.group_by_due import group_by_due, group_by_due_with_order  # todo.group-by-due@^1
todo.summary optional
Counts for the header or footer: "3 items left", percent done, overdue.
Counts for a todo list's header or footer: total, active, done, overdue, due today, percent done and "3 items left".
from fune.todo.summary import summarise_todos  # todo.summary@^1
todo.tag-counts optional
How many todos carry each tag, for a tag sidebar.
How many todos carry each tag, for a tag sidebar: most used first, open todos only unless asked.
from fune.todo.tag_counts import tag_counts  # todo.tag-counts@^1

5. Import and export

Move todos in and out as CSV.

todo.export-csv optional
Writes todos as an RFC 4180 CSV file for backups and spreadsheets.
Write todos as a CSV file (RFC 4180) with one column per field, for backups, spreadsheets and other apps.
from fune.todo.export_csv import export_csv  # todo.export-csv@^1
todo.import-csv optional
Reads that CSV back, checking every row with row-numbered errors.
Read todos from a CSV file (RFC 4180) as todo.export-csv writes it, checking every row, with row-numbered errors.
from fune.todo.import_csv import import_csv, validate_todo_csv  # todo.import-csv@^1
todo.merge optional
Merges imported todos into a list by id.
Merge imported todos into a list by id: an imported todo replaces the one with its id, new ids are appended.
from fune.todo.merge import merge_todos  # todo.merge@^1

6. Calendar arithmetic from dates-basics

Move and measure ISO dates exactly, with no date library and no clock reads.

dates.add-days core
Shifts a date by whole days, for due dates and windows.
Shift an ISO date by a whole number of days, forwards or backwards, with exact calendar arithmetic.
from fune.dates.add_days import add_days  # dates.add-days@^1
dates.add-months core
Adds months clamping to month end, so 31 January plus a month is the end of February.
Add calendar months to an ISO date, clamping to the month end: 31 Jan + 1 month is 28 or 29 Feb.
from fune.dates.add_months import add_months  # dates.add-months@^1
dates.days-between core
Signed calendar days between two dates, for ages of invoices and stays.
Calendar days from one ISO date to another, signed: negative when the second date is earlier.
from fune.dates.days_between import days_between  # dates.days-between@^1
dates.month-boundaries core
First and last day of a date's month, for monthly periods and statements.
The first day, last day and length of the calendar month containing a date.
from fune.dates.month_boundaries import month_boundaries  # dates.month-boundaries@^1
dates.day-of-week core
ISO weekday of a date, for weekend rules and opening hours.
The ISO 8601 day of the week for a date: 1 is Monday through 7 is Sunday.
from fune.dates.day_of_week import day_of_week  # dates.day-of-week@^1
dates.is-leap-year optional
Whether a year is a leap year, for 365/366-day calculations.
Whether a year is a Gregorian leap year: every 4th, except centuries, except every 400th.
from fune.dates.is_leap_year import is_leap_year  # dates.is-leap-year@^1
dates.overlap optional
Days two date ranges share, for prorating and clash checks.
Days two date ranges have in common, with the end of each range stated as inclusive or exclusive.
from fune.dates.overlap import overlap_days  # dates.overlap@^1
dates.age optional
Age in whole years on a date, handling 29 February birthdays.
Age in whole years on a given date, with 29 February birthdays ageing on 1 March in other years.
from fune.dates.age import age  # dates.age@^1

7. Periods and reporting dates from dates-basics

Put dates into the periods reports are grouped by.

dates.quarter core
Calendar and fiscal quarter of a date for any year-start month.
Calendar quarter and fiscal quarter of a date, with the fiscal year and quarter date ranges, for any start month.
from fune.dates.quarter import quarter  # dates.quarter@^1
dates.week-number optional
ISO week number and week-year, for weekly reports.
ISO 8601 week number and week-numbering year of a date: weeks start Monday, week 1 holds the first Thursday.
from fune.dates.week_number import week_number  # dates.week-number@^1
dates.day-count-fraction optional
Year fraction by ACT/365F, ACT/360, 30/360 or ACT/ACT, for interest periods.
Year fraction between two dates by ACT/365F, ACT/360, 30/360, 30E/360 or ACT/ACT ISDA, as an exact fraction.
from fune.dates.day_count_fraction import day_count_fraction  # dates.day-count-fraction@^1

8. Working days and holidays from dates-basics

Skip weekends and public holidays when counting or adding days.

dates.bank-holidays core
UK bank holidays for a region and year, as gov.uk publishes them.
UK bank and public holidays for a region and year, as published by gov.uk, including one-off holidays.
from fune.dates.bank_holidays import bank_holidays  # dates.bank-holidays@^1
dates.add-business-days core
Moves a date by working days, skipping weekends and a holiday list.
Move a date forward or back by working days, skipping weekends and a caller-supplied holiday list.
from fune.dates.add_business_days import add_business_days  # dates.add-business-days@^1
dates.business-days-between core
Working days between two dates, for SLAs and notice periods.
Working days between two dates, excluding weekends and a caller-supplied holiday list.
from fune.dates.business_days_between import business_days_between  # dates.business-days-between@^1

9. Repeating schedules from dates-basics

Generate the dates of something that repeats.

dates.recurrence optional
The next n dates of a rule: monthly on the 31st, last Friday, every two weeks.
The next n dates of a repeating rule: monthly on a day, last weekday of the month, every k days or weeks, yearly.
from fune.dates.recurrence import recurrence  # dates.recurrence@^1

10. Times and durations from dates-basics

Work with times of day, durations and timestamps.

time.duration optional
Parses and adds durations like 1h30m in whole minutes, and formats the total.
Parse durations written as 1h30m, 90m or 01:30, add them up in whole minutes, and format the total.
from fune.time.duration import duration  # time.duration@^1
time.minutes-between optional
Minutes between two local date-times, across midnight.
Minutes from one local date and time to another, correct across midnight; wall-clock time, no time zones.
from fune.time.minutes_between import minutes_between  # time.minutes-between@^1
time.round-to-increment optional
Rounds minutes to a billing increment such as 6 or 15.
Round a duration in minutes to a billing increment such as 6 or 15 minutes, up, down or to nearest.
from fune.time.round_to_increment import round_to_increment  # time.round-to-increment@^1
time.iso-to-unix optional
RFC 3339 timestamps to Unix seconds.
An RFC 3339 timestamp with a Z or offset, such as 2026-09-26T13:00:00+01:00, to Unix time in whole seconds.
from fune.time.iso_to_unix import iso_to_unix  # time.iso-to-unix@^1
time.unix-to-iso optional
Unix seconds to an ISO 8601 UTC timestamp.
Unix time in seconds to an ISO 8601 UTC timestamp such as 2026-09-26T12:00:00Z, for years 0001 to 9999.
from fune.time.unix_to_iso import unix_to_iso  # time.unix-to-iso@^1
time.countdown optional
Seconds left until a moment and a 4:05 style timer text.
Seconds left until a Unix time, whether it has passed, and a timer text like 4:05, for tokens or retry waits.
from fune.time.countdown import countdown  # time.countdown@^1

Gaps

What this kind of app usually needs that Functional Weave does not have yet: write these yourself, or use a service.

  • persistence Storing todos (localStorage, a database) and loading them: caps works on lists in memory.
  • sync Syncing between devices and resolving edits made offline on two of them.
  • notifications Reminders that fire at a time: push, email or OS notifications and their scheduler.
  • sharing Shared lists, assignees and permissions between users.
  • ui The list UI itself: Functional Weave has no todo components, only the logic and React form fields.
  • time-zones Time zones and daylight saving: caps works in ISO dates, UTC and wall-clock times; converting with the IANA tz rules is yours.
  • locale-formatting Showing dates in a user's language and format ("3 octobre 2026"): caps returns ISO dates, not localised text.
  • non-uk-holidays Public holidays outside the UK: dates.bank-holidays is UK only; pass another country's list to the working-day functions yourself.