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 +62 -0
- package/dist/expand.d.ts +34 -0
- package/dist/expand.d.ts.map +1 -0
- package/dist/expand.js +1267 -0
- package/dist/expand.js.map +1 -0
- package/dist/index.d.ts +3 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -45
- package/dist/index.js.map +1 -1
- package/dist/rruleset.d.ts +39 -0
- package/dist/rruleset.d.ts.map +1 -0
- package/dist/rruleset.js +152 -0
- package/dist/rruleset.js.map +1 -0
- package/package.json +1 -1
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
|
package/dist/expand.d.ts
ADDED
|
@@ -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"}
|