Files
skills/docs/foundation/timing/datetime/src/recurrence/recurrence-generate.md
2026-05-29 08:20:56 +01:00

4.0 KiB

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

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

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<Date>

Unified dispatcher. Routes to the frequency-specific generator based on rule.freq. All yielded dates are strictly after from.

generateDaily(rule, from, interval): Generator<Date>

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<Date>

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<Date>

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<Date>

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.