This page explains the core algorithms that power RRStack: how coverage is computed in the cascade, how segments are streamed, how ranges are classified, and how effective bounds are derived — all with timezone/DST‑correct math and unit‑aware operation.
Goals and constraints
[start, end).Baseline (defaultEffect)
'auto' (default): opposite of the first rule’s effect, or 'active' when no rules exist.'active' | 'blackout': use exactly that effect for uncovered instants.Core coverage primitives
options.freq → span rule; duration must be omitted.[starts, ends); either side may be open.options.freq present (daily/weekly/monthly/etc.).dtstart/until/tzid synthesized from JSON (unit/timezone aware).Duration in the rule timezone.Key helpers
epochToWallDate(value, tz, unit) → Date: epoch → floating wall‑clock Date for rrule.floatingDateToZonedEpoch(date, tz, unit) → number: to epoch for comparisons.computeOccurrenceEnd(rule, start) → number: Luxon add in rule tz; rounds up in 's' mode.domainMin(), domainMax(unit): safe guard rails.Cascade evaluation (last‑wins)
t, the cascade status is the effect of the last rule in the list that covers t.Strategy (coverage.ts):
s <= t < e (open sides use domain min/max).t,Streaming, memory‑bounded merge over per‑rule boundary streams (starts/ends). Ends are processed before starts at the same timestamp to preserve last‑wins semantics. Optional limit throws if exceeded. See segments.ts.
Scans streamed segments; early‑exits on mixed coverage. See segments.ts.
See bounds/*.ts for pass details.
's' mode, ends remain exact integer seconds across DST transitions.