# recurrence-generate ## Purpose `recurrence-generate` provides the internal generator functions that produce infinite sequences of candidate dates for each recurrence frequency supported by `@suaveplan/datetime`. Each generator yields dates strictly after a given `from` anchor, and the `generateCandidates` dispatcher routes to the correct generator based on `RecurrenceRule.freq`. This module is an internal implementation detail. Public callers should use `expandRecurrence` and `nextOccurrence` from `recurrence.ts`. ## Features - `generateDaily` — daily step with optional `BYDAY` weekday filter - `generateWeekly` — weekly step, either plain (same weekday) or with `BYDAY` multi-day selection - `generateMonthly` — monthly step supporting `BYMONTHDAY`, `BYDAY`, or plain monthly recurrence - `generateYearly` — yearly step supporting `BYMONTHDAY`, `BYDAY`, or plain yearly recurrence - `generateCandidates` — dispatcher that routes to the correct generator from `RecurrenceRule.freq` - All generators are infinite; termination is enforced by callers (`count` / `until` / `AbortSignal`) ## Usage: Basic Example ```ts import { generateCandidates } from "./recurrence-generate.js"; const rule = { freq: "daily" as const, count: 5 }; const from = new Date(2024, 0, 1); const gen = generateCandidates(rule, from); for (let i = 0; i < 5; i++) { console.log(gen.next().value?.toDateString()); } // Tue Jan 02 2024 … Sat Jan 06 2024 ``` ## Usage: Advanced Example ```ts import { generateCandidates } from "./recurrence-generate.js"; // Monthly with BYMONTHDAY — yields the 15th of each month const rule = { freq: "monthly" as const, byMonthDay: [15], }; const from = new Date(2024, 0, 1); const gen = generateCandidates(rule, from); const first = gen.next().value!; console.log(first.toDateString()); // Mon Jan 15 2024 const second = gen.next().value!; console.log(second.toDateString()); // Thu Feb 15 2024 // Weekly with BYDAY — yields Mon/Wed/Fri const weeklyRule = { freq: "weekly" as const, byDay: [1, 3, 5] as const, }; const weeklyGen = generateCandidates(weeklyRule, new Date(2024, 2, 11)); console.log(weeklyGen.next().value?.getDay()); // 3 (Wed Mar 13) ``` ## API Reference ### `generateCandidates(rule: RecurrenceRule, from: Date): Generator` Unified dispatcher. Routes to the frequency-specific generator based on `rule.freq`. All yielded dates are strictly after `from`. ### `generateDaily(rule, from, interval): Generator` Steps `interval` days at a time. If `rule.byDay` is set, only yields dates whose `getDay()` is in the list. ### `generateWeekly(rule, from, interval): Generator` If `rule.byDay` is set: anchors to the Sunday of the current week, then emits the specified weekdays in sorted order each `interval * 7` days. Without `byDay`: yields the same weekday every `interval * 7` days. ### `generateMonthly(rule, from, interval): Generator` Three modes: 1. `byMonthDay` — emits the specified day-of-month values (sorted) in each month, skipping months where a day exceeds `daysInMonth`. 2. `byDay` — scans each month and emits all days matching the weekday list. 3. Plain — yields the same day-of-month each `interval` months using `addMonths` clamping. ### `generateYearly(rule, from, interval): Generator` Three modes: 1. `byMonthDay` — emits specified day-of-month values in each year. 2. `byDay` — advances one day at a time from the year anchor, yielding matching weekdays. 3. Plain — yields the same date each `interval` years using `addYears`. ## Implementation Notes All generators are implemented as native ES `Generator` functions (`function*`) to enable lazy evaluation. They produce an infinite sequence; the caller (`expandRecurrence`, `nextOccurrence`) is responsible for applying `count`, `until`, and `AbortSignal` termination conditions. The `generateWeekly` with `BYDAY` mode anchors to the start of the week (Sunday) on the first iteration, which means it may emit dates in the current week before advancing. Dates before or equal to `from` are filtered out with a `candidate > from` guard.