Recurrence rule (RFC 5545) processing built on the Temporal API.
The library accepts the familiar RRULE
format and returns
Temporal.ZonedDateTime
instances for easy time‑zone aware scheduling.
See the demo site for an interactive playground.
This was created to advance the rrule library to use Temporal, and to provide a more modern API, as the original rrule library is not maintained anymore. Maintainers suggested to use Temporal instead of Date: jkbrzt/rrule#450 (comment)
npm install rrule-temporal
Parse an ICS snippet and enumerate the occurrences:
import { RRuleTemporal } from "rrule-temporal";
const rule = new RRuleTemporal({
rruleString: `DTSTART;TZID=UTC:20250101T090000\nRRULE:FREQ=DAILY;COUNT=3`
});
rule.all().forEach(dt => console.log(dt.toString()));
// 2025-01-01T09:00:00[UTC]
// 2025-01-02T09:00:00[UTC]
// 2025-01-03T09:00:00[UTC]
Instead of a full ICS string you can supply the recurrence parameters directly:
import { Temporal } from "@js-temporal/polyfill";
const rule = new RRuleTemporal({
freq: "DAILY",
interval: 2,
count: 3,
byHour: [9],
byMinute: [15],
tzid: "America/Chicago",
dtstart: Temporal.ZonedDateTime.from({
year: 2025, month: 4, day: 20,
hour: 8, minute: 30,
timeZone: "America/Chicago"
})
});
rule.all().forEach(dt => console.log(dt.toString()));
Use the provided methods to enumerate or search for occurrences:
// Get all events within a window
const start = new Date(Date.UTC(2025, 3, 2, 0, 0));
const end = new Date(Date.UTC(2025, 3, 4, 5, 0));
const hits = rule.between(start, end, true);
// Next and previous occurrences
const next = rule.next();
const prev = rule.previous(new Date("2025-05-01T00:00Z"));
rule.toString(); // DTSTART and RRULE lines
rule.toText(); // human readable description
Method | Description |
---|---|
new RRuleTemporal(opts) |
Create a rule from an ICS snippet or manual options. |
all(iterator?) |
Return every occurrence. When the rule has no end the optional iterator is required. |
between(after, before, inclusive?) |
Occurrences within a time range. |
next(after?, inclusive?) |
Next occurrence after a given date. |
previous(before?, inclusive?) |
Previous occurrence before a date. |
toString() |
Convert the rule back into DTSTART and RRULE lines. |
toText(formatter?) |
English description of the rule. |
options() |
Return the normalized options object. |
Enumerating weekdays within a month or rotating through months can be achieved with the more advanced RFC 5545 fields:
// 2nd & 4th Fridays each month at midnight CT, first 6 occurrences
const ruleA = new RRuleTemporal({
rruleString: `DTSTART;TZID=America/Chicago:20250325T000000\nRRULE:FREQ=MONTHLY;BYDAY=2FR,4FR;BYHOUR=0;BYMINUTE=0;COUNT=6`
});
ruleA.all().forEach(dt => console.log(dt.toString()));
// Rotate yearly through Jan, Jun and Dec at 09:00 UTC
const dtstart = Temporal.ZonedDateTime.from({
year: 2025, month: 1, day: 10, hour: 9, minute: 0, timeZone: "UTC"
});
const ruleB = new RRuleTemporal({
freq: "YEARLY",
interval: 1,
count: 4,
byMonth: [1, 6, 12],
byHour: [9],
byMinute: [0],
dtstart
});
ruleB.all().forEach(dt => console.log(dt.toString()));