Python API

Reference for the dateme Python package.

Exports

Top-level package exports:

Name

Description

Schedule

Query engine class.

model

Typed builder module.

Weekday, Nth, Makeup, MakeupFailure, OverlayRule, CalendarId

Enum values for typed builders.

MonthDay, NthWeekday

Date-position builder types.

Overlay, AnyOverlay

Overlay builder types.

CalendarDates, CalendarUnion, CalendarDiff, CustomCalendar

Calendar spec builder types.

Hourly, Daily, Weekly, EveryNDays, EveryNWeeks

Frequency builder types.

MonthlyByDay, MonthlyByWeekday, Yearly, Quarterly, CustomCron

Frequency builder types.

MakeupStep, WeekdayMakeup

Makeup builder types.

Schedule

Schedule is the Python wrapper around the Rust recurrence engine.

Constructor

Schedule(spec, calendar_provider=None)

Parameters:

Parameter

Type

Description

spec

JSON str, dict, or object to_dict()

Schedule model.

calendar_provider

callable or object, optional

Provider for { "custom": "name" } calendars.

The constructor validates the schedule. Invalid JSON, invalid enum values, invalid timezone names, and structural validation failures raise ValueError.

Examples:

from dateme import Schedule

schedule = Schedule({
    "freq": {"type": "daily", "time": "09:00"},
    "timezone": "UTC",
})
from dateme import Schedule, Weekly, Weekday, Overlay, CalendarId, OverlayRule, Makeup
from dateme import model as m

spec = m.Schedule(
    freq=Weekly([Weekday.MON], "17:30"),
    timezone="America/New_York",
    overlays=[Overlay(CalendarId.NYSE_HOLIDAY, OverlayRule.EXCLUDE)],
    makeup=Makeup.AFTER,
)
schedule = Schedule(spec)

Custom Calendar Providers

Custom calendars are referenced in a schedule with { "custom": "name" }. Providers receive (name, date) where date is a YYYY-MM-DD string.

A callable provider:

schedule = Schedule(
    {
        "freq": {"type": "daily", "time": "09:00"},
        "timezone": "UTC",
        "overlays": [{"calendar": {"custom": "shutdown"}, "rule": "exclude"}],
    },
    lambda name, date: name == "shutdown" and date == "2026-08-14",
)

An object provider:

class Calendars:
    def contains(self, name: str, date: str) -> bool:
        return name == "shutdown" and date in {"2026-08-14", "2026-08-15"}


schedule = Schedule(spec, Calendars())

Missing custom calendar values are treated as absent from the set.

Datetime Handling

Rule

Behavior

Input datetimes

Timezone-aware datetime values are expected.

Naive inputs

Interpreted as UTC by PyO3 datetime conversion.

Returned values

Timezone-aware UTC datetime values.

Optional anchors

Default to the current UTC time.

Query bounds

Strict: occurrences exactly at after or before are excluded.

Methods

Schedule.from_json

Schedule.from_json(json, calendar_provider=None) -> Schedule

Builds a schedule from a JSON string.

schedule = Schedule.from_json('{"freq":{"type":"daily","time":"09:00"},"timezone":"UTC"}')

Schedule.from_dict

Schedule.from_dict(spec, calendar_provider=None) -> Schedule

Builds a schedule from a dict or typed builder object.

schedule = Schedule.from_dict({"freq": {"type": "daily", "time": "09:00"}, "timezone": "UTC"})

to_json

schedule.to_json() -> str

Returns the JSON representation.

blob = schedule.to_json()
again = Schedule.from_json(blob)

to_dict

schedule.to_dict() -> dict

Returns the schedule model as a Python dictionary.

spec = schedule.to_dict()

validate

schedule.validate() -> None

Re-runs structural validation. Raises ValueError on failure.

schedule.validate()

next

schedule.next(after=None) -> datetime | None

Returns the first occurrence strictly after after.

from datetime import datetime, timezone

after = datetime(2026, 1, 13, tzinfo=timezone.utc)
schedule.next(after)

previous

schedule.previous(before=None) -> datetime | None

Returns the last occurrence strictly before before.

before = datetime(2026, 1, 13, tzinfo=timezone.utc)
schedule.previous(before)

until

schedule.until(before, after=None) -> list[datetime]

Returns occurrences in (after, before), ascending.

end = datetime(2026, 2, 1, tzinfo=timezone.utc)
schedule.until(end, after)

since

schedule.since(after, before=None) -> list[datetime]

Returns occurrences in (after, before), descending.

start = datetime(2026, 1, 1, tzinfo=timezone.utc)
schedule.since(start)

upcoming

schedule.upcoming(n, after=None) -> list[datetime]

Returns the next n occurrences strictly after after, ascending.

schedule.upcoming(5, after)

Trace Methods

Trace methods return dictionaries with instant and reason.

Method

Return type

Order

next_trace(after=None)

dict | None

previous_trace(before=None)

dict | None

until_trace(before, after=None)

list[dict]

ascending

since_trace(after, before=None)

list[dict]

descending

upcoming_trace(n, after=None)

list[dict]

ascending

Reason strings include:

Reason form

Meaning

base

Base occurrence was kept.

makeup_from(YYYY-MM-DD)

Occurrence was moved from the local date.

base,shifted_dst

Base occurrence was shifted through DST gap.

makeup_from(...),shifted_dst

Made-up occurrence was shifted through DST gap.

trace = schedule.next_trace(after)
# {"instant": datetime(..., tzinfo=timezone.utc), "reason": "base"}

is_occurrence

schedule.is_occurrence(instant) -> bool

Returns whether instant is an occurrence of the schedule.

schedule.is_occurrence(datetime(2026, 1, 20, 22, 30, tzinfo=timezone.utc))

count_between

schedule.count_between(after, before) -> int

Returns the number of occurrences strictly in (after, before).

schedule.count_between(after, end)

describe

schedule.describe() -> str

Returns a human-readable summary of the base recurrence, timezone, overlay count, and makeup presence.

schedule.describe()
# "Every Monday at 17:30 America/New_York, with 1 overlay(s), with makeup"

Iteration

iter(schedule) -> iterator[datetime]
schedule.iter_between(after, before) -> iterator[datetime]
schedule.iter_upcoming(n, after=None) -> iterator[datetime]
instant in schedule -> bool

iter(schedule) requires the schedule to have an end bound. It starts from start, or from the current UTC time when start is absent.

for instant in bounded_schedule:
    print(instant)

list(schedule.iter_between(after, end))
list(schedule.iter_upcoming(3, after))
instant in schedule

Typed Model

dateme.model mirrors the Schedule model as dataclasses and enums. Builders expose to_dict() and schedules expose to_json().

from dateme import (
    Schedule,
    MonthlyByWeekday,
    NthWeekday,
    Nth,
    Weekday,
    MonthDay,
    Overlay,
    CalendarId,
    OverlayRule,
    Makeup,
)
from dateme import model as m

spec = m.Schedule(
    freq=MonthlyByWeekday([NthWeekday(Nth.THIRD, Weekday.FRI)], "16:00"),
    timezone="America/New_York",
    overlays=[Overlay(CalendarId.NYSE_TRADING_DAY, OverlayRule.ONLY)],
    makeup=Makeup.NONE,
)
schedule = Schedule(spec)

Builder families:

Family

Builders

Frequencies

Hourly, Daily, Weekly, EveryNDays, EveryNWeeks, MonthlyByDay, MonthlyByWeekday, Yearly, Quarterly, CustomCron

Calendar specs

CalendarId, CalendarDates, CalendarUnion, CalendarDiff, CustomCalendar

Overlays

Overlay, AnyOverlay, OverlayRule

Makeup

Makeup, MakeupFailure, MakeupStep, WeekdayMakeup

Date helpers

MonthDay, NthWeekday, Nth, Weekday

class dateme.Schedule(spec, calendar_provider=None)

Bases: object

A recurrence schedule built from its JSON representation.

A schedule is a frequency in an IANA timezone, plus optional calendar overlays, a makeup strategy, and start/end bounds. Its methods compute the instants the schedule fires. Reference instants are timezone-aware datetime objects; when omitted they default to the current time (UTC).

__contains__(key, /)

Return key in self.

__iter__()

Implement iter(self).

count_between(after, before)

Count occurrences strictly in (after, before).

describe()

Human-readable summary.

static from_dict(spec, calendar_provider=None)

Build a schedule from a dict or typed dateme.model builder.

static from_json(json, calendar_provider=None)

Build a schedule from its JSON representation.

is_occurrence(instant)

Whether instant is an occurrence of this schedule.

iter_between(after, before)

Iterate occurrences in (after, before), ascending.

iter_upcoming(n, after=None)

Iterate the next n occurrences strictly after after (default: now).

next(after=None)

First occurrence strictly after after (default: now).

next_trace(after=None)

First occurrence trace strictly after after (default: now).

previous(before=None)

Last occurrence strictly before before (default: now).

previous_trace(before=None)

Last occurrence trace strictly before before (default: now).

since(after, before=None)

Occurrences in (after, before), descending. since(start)[0] == previous().

since_trace(after, before=None)

Occurrence traces in (after, before), descending.

to_dict()

Serialize back to a dict.

to_json()

Serialize back to a JSON string.

until(before, after=None)

Occurrences in (after, before), ascending. until(end)[0] == next().

until_trace(before, after=None)

Occurrence traces in (after, before), ascending.

upcoming(n, after=None)

The next n occurrences strictly after after (default: now), ascending.

upcoming_trace(n, after=None)

The next n occurrence traces strictly after after (default: now), ascending.

validate()

Structural validation. Raises ValueError on an invalid schedule.

Typed builders for the dateme schedule model.

These dataclasses and enums mirror the JSON schedule model. Build a structure from them and pass it to dateme.Schedule (or call to_dict() / to_json()). Construction performs light validation; the authoritative structural check remains dateme.Schedule.validate().

class dateme.model.Weekday(value)[source]

Bases: str, Enum

class dateme.model.Nth(value)[source]

Bases: str, Enum

class dateme.model.Makeup(value)[source]

Bases: str, Enum

class dateme.model.MakeupStep(direction: Makeup, max_hops: int | None = None)[source]

Bases: object

One cascade makeup step.

class dateme.model.WeekdayMakeup(mon: Makeup | None = None, tue: Makeup | None = None, wed: Makeup | None = None, thu: Makeup | None = None, fri: Makeup | None = None, sat: Makeup | None = None, sun: Makeup | None = None, default: Makeup | None = None)[source]

Bases: object

Makeup directions selected by the excluded date’s weekday.

class dateme.model.OverlayRule(value)[source]

Bases: str, Enum

class dateme.model.CalendarId(value)[source]

Bases: str, Enum

class dateme.model.CalendarDates(dates: list[str | date | datetime])[source]

Bases: object

An inline set of local dates.

class dateme.model.CalendarUnion(union: list[CalendarId | CalendarDates | CalendarUnion | CalendarDiff | CustomCalendar])[source]

Bases: object

A calendar that contains dates present in any child calendar.

class dateme.model.CalendarDiff(diff: list[CalendarId | CalendarDates | CalendarUnion | CalendarDiff | CustomCalendar])[source]

Bases: object

A calendar containing dates in the first child, minus following children.

class dateme.model.CustomCalendar(custom: str)[source]

Bases: object

A named calendar resolved by a runtime provider.

class dateme.model.MakeupFailure(value)[source]

Bases: str, Enum

class dateme.model.MonthDay(value: int | None = None)[source]

Bases: object

A day within a month: a fixed value (1-31), or the last day when None.

class dateme.model.NthWeekday(nth: Nth, weekday: Weekday)[source]

Bases: object

An ordinal weekday within a month, e.g. the third Tuesday.

class dateme.model.Overlay(calendar: CalendarId | CalendarDates | CalendarUnion | CalendarDiff | CustomCalendar, rule: OverlayRule, makeup: Makeup | WeekdayMakeup | list[Makeup | MakeupStep] | None = None)[source]

Bases: object

A calendar filter applied to occurrences.

class dateme.model.AnyOverlay(any: list[Overlay | AnyOverlay], makeup: Makeup | WeekdayMakeup | list[Makeup | MakeupStep] | None = None)[source]

Bases: object

An overlay group that passes when any child overlay passes.

class dateme.model.Frequency[source]

Bases: object

Base class for the recurrence frequencies.

class dateme.model.Hourly(minute: int)[source]

Bases: Frequency

Every hour, at minute past the hour.

class dateme.model.Daily(time: str | time)[source]

Bases: Frequency

Every day at time.

class dateme.model.Weekly(days: list[Weekday], time: str | time)[source]

Bases: Frequency

Every selected weekday at time.

class dateme.model.EveryNDays(interval: int, start_date: str | date | datetime, time: str | time)[source]

Bases: Frequency

Every interval days from start_date at time.

class dateme.model.EveryNWeeks(interval: int, start_date: str | date | datetime, days: list[Weekday], time: str | time)[source]

Bases: Frequency

Every interval weeks from start_date on selected weekdays.

class dateme.model.MonthlyByDay(days: list[MonthDay], time: str | time)[source]

Bases: Frequency

Selected days-of-month at time.

class dateme.model.MonthlyByWeekday(weekdays: list[NthWeekday], time: str | time)[source]

Bases: Frequency

Selected nth-weekdays at time.

class dateme.model.Yearly(month: int, day: MonthDay, time: str | time)[source]

Bases: Frequency

Once a year in month on day at time.

class dateme.model.Quarterly(month: int, day: MonthDay, time: str | time)[source]

Bases: Frequency

Every quarter on month within quarter (1-3), on day at time.

class dateme.model.CustomCron(expr: str)[source]

Bases: Frequency

Five-field cron expression in schedule-local time.

class dateme.model.Schedule(freq: ~dateme.model.Frequency, timezone: str, overlays: list[~dateme.model.Overlay | ~dateme.model.AnyOverlay] = <factory>, makeup: ~dateme.model.Makeup | ~dateme.model.WeekdayMakeup | list[~dateme.model.Makeup | ~dateme.model.MakeupStep] = Makeup.NONE, max_makeup_hops: int | None = None, makeup_failure: ~dateme.model.MakeupFailure = MakeupFailure.SKIP, makeup_only_on: list[~dateme.model.Weekday] | None = None, makeup_within_week: bool = False, makeup_exclude_weekends: bool = False, makeup_before_next: bool = False, skip_if_consecutive_excluded: int | None = None, max_skip_gap: int | None = None, start: str | ~datetime.datetime | None = None, end: str | ~datetime.datetime | None = None)[source]

Bases: object

A complete schedule spec.

Pass an instance to dateme.Schedule to compute occurrences, or call to_dict() / to_json() for the storable form.