Repository navigation
This patch fixes includeDtstart dropping a DTSTART that BYSETPOS skips, and accepts quoted TZID parameters. There are no API changes.
includeDtstart with BYSETPOS
With includeDtstart: true, DTSTART is emitted and counted within COUNT even when the rule wouldn't generate it. That failed when DTSTART matched every BYxxx part but BYSETPOS didn't select it: DTSTART was dropped and COUNT filled with a later occurrence instead. For example, a Friday DTSTART for a last-Friday rule:
DTSTART;TZID=Australia/Sydney:20261009T090000
RRULE:FREQ=MONTHLY;BYDAY=FR;BYSETPOS=-1;COUNT=4
all() with includeDtstart: true |
|
|---|---|
| v2.2.7 | 2026-10-30, 2026-11-27, 2026-12-25, 2027-01-29 |
| v2.2.8 | 2026-10-09, 2026-10-30, 2026-11-27, 2026-12-25 |
The fix applies to generation and to every query method: next(), previous(), between(), matches(), occursOn(), and all() with a callback. The behavior dates back to at least 2.0.2. Rules without BYSETPOS, and a DTSTART that BYSETPOS does select, are unchanged. Thanks to @m0dded for the detailed report in #150. (#151)
Quoted TZID parameters
DTSTART;TZID="America/New_York":20260307T090000 used to throw RangeError: Invalid TimeZone, and so did the same parameter on EXDATE and RDATE. RFC 5545 allows parameter values to be quoted, so the quotes are now removed when parsing. toString() still writes TZID without quotes. Thanks to @bensynapse for the fix. (#144)
Validation
- 1,607 tests pass on Node 20 and 24 (bundled polyfill) and Node 26 (native Temporal).
- A differential over 570 BYSETPOS rules compares every query method with the rule generated without
includeDtstart, plus DTSTART counted within COUNT. It covers six frequencies, three time zones, and both COUNT and UNTIL. 2,382 of its 4,549 checks failed on v2.2.7; none fail now. - 151,168 query-window fuzz comparisons, half of them with
includeDtstart, found no mismatches.
Full changelog: v2.2.7...v2.2.8