Calendar Recurrence Rules (RRULE)

The scheduler’s cron is interval-style 5-field only — it cannot express “every 2nd Tuesday”, “the last weekday of the month”, or “every weekday for 10 occurrences”. This adds an RFC 5545 (iCalendar) RRULE parser and occurrence expander, the calendar layer above cron.

Supported rule parts: FREQ (DAILY/WEEKLY/MONTHLY/YEARLY), INTERVAL, COUNT, UNTIL, BYDAY (incl. ordinals like 2MO / -1FR), BYMONTHDAY (incl. negatives), BYMONTH, BYSETPOS and WKST. Time-level parts and BYWEEKNO/BYYEARDAY are out of scope. Pure standard library (datetime + calendar); the clock is injectable so next_occurrence is deterministic. Imports no PySide6.

Headless API

import datetime
from je_auto_control import parse_rrule, occurrences, next_occurrence

rule = parse_rrule("FREQ=MONTHLY;BYDAY=2TU")     # every 2nd Tuesday
start = datetime.datetime(2026, 1, 1, 9, 0)

for moment in occurrences(rule, start, count=3):
    print(moment)            # 2026-01-13 09:00, 2026-02-10 09:00, ...

# "last weekday of the month"
last = parse_rrule("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")

# next fire at or after a given time (inject now for determinism)
nxt = next_occurrence(rule, start, now=datetime.datetime(2026, 3, 15))

parse_rrule accepts the rule with or without the RRULE: prefix and returns a frozen Recurrence. occurrences yields datetimes anchored at dtstart (its time-of-day and timezone are applied to every occurrence), bounded by COUNT / UNTIL (or the count= / until= overrides) and a safety cap. A date-only UNTIL bounds the whole day inclusively. next_occurrence returns the first occurrence at or after now.

Executor commands

AC_rrule_occurrences takes rule and an ISO dtstart (plus optional count) and returns {occurrences} as ISO datetimes. AC_rrule_next takes rule / dtstart / optional now and returns {next}. Both are exposed as MCP tools (ac_rrule_occurrences / ac_rrule_next) and as Script Builder commands under Flow.