rrule-ts 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,6 +45,68 @@ console.log(stringify(result.value))
45
45
  | `rrule-ts/locales/en` | English locale pack (planned) |
46
46
  | `rrule-ts/locales/de` | German locale pack (planned) |
47
47
 
48
+ ## Timezones and DST
49
+
50
+ ### Temporal and polyfill setup
51
+
52
+ rrule-ts is Temporal-native and has zero runtime dependencies. Temporal is obtained through
53
+ `getTemporal()`, which returns `globalThis.Temporal` when it is present (Node.js 26+, modern browsers
54
+ with native Temporal) and otherwise returns the implementation injected by the caller via
55
+ `setTemporal()`. If neither is available, `getTemporal()` throws a descriptive error.
56
+
57
+ On Node.js < 26 or older browsers, install a polyfill and inject it once at application startup,
58
+ before any RRULE expansion:
59
+
60
+ ```ts
61
+ import { setTemporal } from 'rrule-ts'
62
+ import { Temporal } from 'temporal-polyfill'
63
+
64
+ setTemporal(Temporal)
65
+ ```
66
+
67
+ ### Wall-clock semantics
68
+
69
+ rrule-ts uses wall-clock semantics throughout. For DAILY and coarser frequencies (WEEKLY, MONTHLY,
70
+ YEARLY), the engine advances a `PlainDate` counter by the rule interval and pairs each date with the
71
+ same target time components (hour, minute, second) taken from the `DTSTART`. Occurrences are then
72
+ resolved to `ZonedDateTime` values by calling `Temporal.ZonedDateTime.from` with those components and
73
+ the rule's timezone. As a result, a daily 09:00 event stays at 09:00 local time before and after a
74
+ DST transition, even though the UTC offset changes.
75
+
76
+ Sub-daily frequencies (HOURLY, MINUTELY, SECONDLY) also advance using wall-clock arithmetic on the
77
+ time fields. The UTC-elapsed distance between consecutive occurrences can therefore vary across DST
78
+ boundaries.
79
+
80
+ ### Fall-back (repeated hour)
81
+
82
+ When a computed local time falls in a "fall back" ambiguity (the clock is set back and a local hour
83
+ occurs twice), rrule-ts resolves the ambiguity with `disambiguation: 'compatible'`. The
84
+ `'compatible'` setting selects the earlier of the two possible instants (equivalent to fold=0 in
85
+ Python's datetime). This matches python-dateutil's fall-back behavior exactly.
86
+
87
+ ### Spring-forward (skipped hour)
88
+
89
+ When a computed local time falls in the DST gap created by a spring-forward transition (a local time
90
+ that does not exist), rrule-ts again applies `disambiguation: 'compatible'`. For a skipped time,
91
+ `'compatible'` shifts the wall-clock time forward to the first valid instant after the gap. For
92
+ example, 02:30 in a zone where clocks jump from 02:00 to 03:00 is normalised to 03:30 with the
93
+ post-gap offset. Both times correspond to the same epoch milliseconds, but the ISO strings differ.
94
+
95
+ This is the one deliberate deviation from python-dateutil. When dateutil encounters a nonexistent
96
+ local time, it preserves the original wall-clock digits with the pre-gap UTC offset (e.g.
97
+ `02:30-08:00` during the Los Angeles spring-forward). The resulting ISO string describes an instant
98
+ that does not exist in the timezone. rrule-ts, via Temporal, always emits a valid instant. The
99
+ epoch milliseconds are identical between the two representations, so downstream consumers that
100
+ compare by timestamp are unaffected.
101
+
102
+ This deviation is covered by three documented conformance cases in `packages/conformance`:
103
+
104
+ | Case ID | Timezone | Description |
105
+ |---|---|---|
106
+ | `dst-la-spring-daily` | `America/Los_Angeles` | 02:30 in spring-forward gap; dateutil: `02:30-08:00`; rrule-ts: `03:30-07:00` |
107
+ | `dst-berlin-spring-daily` | `Europe/Berlin` | 02:00 in spring-forward gap; dateutil: `02:00+01:00`; rrule-ts: `03:00+02:00` |
108
+ | `dst-lord-howe-spring-daily` | `Australia/Lord_Howe` | 02:00 in 30-min gap; dateutil: `02:00+10:30`; rrule-ts: `02:30+11:00` |
109
+
48
110
  ## License
49
111
 
50
112
  MIT
@@ -0,0 +1,34 @@
1
+ import type { RRuleOptions, RRuleDtstart } from './types.js';
2
+ /**
3
+ * Lazily iterate over RRULE occurrences.
4
+ *
5
+ * Yields Temporal values matching the dtstart type: Temporal.PlainDate,
6
+ * Temporal.PlainDateTime, Temporal.Instant, or Temporal.ZonedDateTime.
7
+ */
8
+ export declare function iterate(options: RRuleOptions): Generator<RRuleDtstart>;
9
+ /**
10
+ * Options for controlling which occurrences are returned by expand().
11
+ */
12
+ export interface ExpandOptions {
13
+ /** Maximum number of occurrences to return. */
14
+ limit?: number;
15
+ /** Only return occurrences after this value. */
16
+ after?: RRuleDtstart;
17
+ /** Only return occurrences before (or at) this value. */
18
+ before?: RRuleDtstart;
19
+ /** Whether `after` and `before` bounds are inclusive (default true). */
20
+ inclusive?: boolean;
21
+ }
22
+ /**
23
+ * Materialize RRULE occurrences into an array.
24
+ *
25
+ * The second argument can be:
26
+ * - A plain number: the maximum number of occurrences to return (a hard limit
27
+ * applied AFTER COUNT/UNTIL from the rule itself).
28
+ * - An ExpandOptions object with optional `limit`, `after`, `before`,
29
+ * `inclusive` fields.
30
+ *
31
+ * Returns an array of Temporal values matching the dtstart type.
32
+ */
33
+ export declare function expand(options: RRuleOptions, limitOrOpts?: number | ExpandOptions): RRuleDtstart[];
34
+ //# sourceMappingURL=expand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../src/expand.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAuB,MAAM,YAAY,CAAA;AAw+BjF;;;;;GAKG;AACH,wBAAiB,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC,CA2XvE;AA6CD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,YAAY,EACrB,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,GACnC,YAAY,EAAE,CAkChB"}