Functional Weave
Code in TypeScript

todo.next-occurrence@1.0.0

impl/python.py

1,599 bytes · the Python implementation · view 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 add_days  ← 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 .subscriptions_next_billing_date import next_billing_date  ← from subscriptions.next-billing-date ^1.0.0 · built alongside by fune
from .todo_item import Recurrence  ← from todo.item ^1.0.0 · built alongside by fune

_STEPS = {"daily": "day", "weekly": "week", "monthly": "month", "yearly": "year"}


def next_occurrence(recurrence: Recurrence, due: str, today: str) -> str:
    """The first date of the series strictly after both due and today.

    Every occurrence is the anchor plus whole intervals, so month ends stay
    month ends; finishing late skips the occurrences already missed.
    """
    frequency, interval, anchor = recurrence.frequency, recurrence.interval, recurrence.anchor
    if frequency != "weekdays" and frequency not in _STEPS:
        raise ValueError(f'unknown repeat frequency "{frequency}": expected daily, weekdays, weekly, monthly or yearly')
    if isinstance(interval, bool) or not isinstance(interval, int) or interval < 1:
        raise ValueError(f"repeat interval must be 1 or more, received {interval}")
    if frequency == "weekdays" and interval != 1:
        raise ValueError(f"weekday repeats cannot skip weeks: interval must be 1, received {interval}")
    add_days(anchor, 0)
    add_days(due, 0)
    add_days(today, 0)
    if anchor > due:
        raise ValueError(f"repeat anchor {anchor} is after the due date {due}")

    after = due if due > today else today
    if frequency == "weekdays":
        nxt = add_days(after, 1)
        while day_of_week(nxt) > 5:
            nxt = add_days(nxt, 1)
        return nxt
    return next_billing_date(anchor, _STEPS[frequency], interval, after)