temporal-fmt 0.9.2 → 0.9.4
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 +480 -9
- package/dist/calendarUtils.cjs +259 -4
- package/dist/calendarUtils.cjs.map +1 -1
- package/dist/calendarUtils.js +33 -1
- package/dist/chunk-4N3WCGEY.js +183 -0
- package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
- package/dist/chunk-5UTV5VP6.js +984 -0
- package/dist/chunk-5UTV5VP6.js.map +1 -0
- package/dist/chunk-ACKKWH5Z.js +578 -0
- package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
- package/dist/chunk-EGUUO6LE.js +439 -0
- package/dist/chunk-EGUUO6LE.js.map +1 -0
- package/dist/chunk-FBPHNRVW.js +2939 -0
- package/dist/chunk-FBPHNRVW.js.map +1 -0
- package/dist/chunk-GPB4JHGI.js +65 -0
- package/dist/chunk-GPB4JHGI.js.map +1 -0
- package/dist/chunk-H7YQX25T.js +170 -0
- package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
- package/dist/chunk-HXY2GUUM.js +182 -0
- package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
- package/dist/chunk-L24NPTAJ.js +328 -0
- package/dist/chunk-L24NPTAJ.js.map +1 -0
- package/dist/chunk-M3GDTHMT.js +169 -0
- package/dist/chunk-M3GDTHMT.js.map +1 -0
- package/dist/chunk-MKPDLQKD.js +51 -0
- package/dist/chunk-MKPDLQKD.js.map +1 -0
- package/dist/chunk-TSLPIURC.js +242 -0
- package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
- package/dist/chunk-UOPAV3WX.js +75 -0
- package/dist/chunk-UOPAV3WX.js.map +1 -0
- package/dist/chunk-VNIUIQA4.js +398 -0
- package/dist/chunk-VNIUIQA4.js.map +1 -0
- package/dist/chunk-YEUASGDK.js +9 -0
- package/dist/chunk-YEUASGDK.js.map +1 -0
- package/dist/chunk-ZQZF5EXW.js +29 -0
- package/dist/chunk-ZQZF5EXW.js.map +1 -0
- package/dist/config.d.cts +4 -2
- package/dist/config.d.ts +4 -2
- package/dist/duration.cjs +638 -26
- package/dist/duration.cjs.map +1 -1
- package/dist/duration.js +28 -1
- package/dist/format.cjs +833 -35
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +20 -1
- package/dist/index.cjs +7698 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -24
- package/dist/index.d.ts +108 -24
- package/dist/index.js +1536 -58
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +5480 -35
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +46 -1
- package/dist/localeRegistry.cjs +375 -32
- package/dist/localeRegistry.cjs.map +1 -1
- package/dist/localeRegistry.js +11 -1
- package/dist/modApi.d.cts +131 -0
- package/dist/modApi.d.ts +131 -0
- package/dist/parse.cjs +1797 -70
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +20 -1
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/recurrence.cjs +452 -20
- package/dist/recurrence.cjs.map +1 -1
- package/dist/recurrence.js +19 -1
- package/dist/relativeTime.cjs +268 -21
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +14 -1
- package/dist/runtime.d.cts +201 -0
- package/dist/runtime.d.ts +201 -0
- package/dist/timezone.cjs +387 -20
- package/dist/timezone.cjs.map +1 -1
- package/dist/timezone.js +26 -1
- package/package.json +4 -6
- package/scripts/cli.mjs +27 -5
- package/dist/chunk-2JHEV7ZF.js +0 -7
- package/dist/chunk-3MZLTVP3.js +0 -2
- package/dist/chunk-3MZLTVP3.js.map +0 -1
- package/dist/chunk-4JWGUR4O.js +0 -11
- package/dist/chunk-5U5WJ465.js +0 -36
- package/dist/chunk-5U5WJ465.js.map +0 -1
- package/dist/chunk-C5YESWFT.js +0 -2
- package/dist/chunk-C5YESWFT.js.map +0 -1
- package/dist/chunk-C6ZZ62ET.js +0 -2
- package/dist/chunk-C6ZZ62ET.js.map +0 -1
- package/dist/chunk-EHLRZULM.js +0 -5
- package/dist/chunk-F4RGUDA3.js +0 -2
- package/dist/chunk-F4RGUDA3.js.map +0 -1
- package/dist/chunk-J2AQNXF2.js +0 -2
- package/dist/chunk-NBXF7V5B.js +0 -2
- package/dist/chunk-NBXF7V5B.js.map +0 -1
- package/dist/chunk-R52YOKI3.js +0 -7
- package/dist/chunk-R52YOKI3.js.map +0 -1
- package/dist/chunk-UJR6DHXY.js +0 -2
- package/dist/chunk-UJR6DHXY.js.map +0 -1
- package/dist/chunk-VQXUMFB2.js +0 -21
- package/dist/chunk-XH46JNPK.js +0 -2
- package/dist/chunk-XH46JNPK.js.map +0 -1
- package/dist/chunk-YB6YAG7E.js +0 -35
- package/dist/chunk-YB6YAG7E.js.map +0 -1
- package/dist/chunk-ZGMTY52O.js +0 -2
- package/dist/chunk-ZGMTY52O.js.map +0 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import {
|
|
2
|
+
add
|
|
3
|
+
} from "./chunk-TSLPIURC.js";
|
|
4
|
+
import {
|
|
5
|
+
asDateFieldView
|
|
6
|
+
} from "./chunk-HXY2GUUM.js";
|
|
7
|
+
|
|
8
|
+
// src/comparison.ts
|
|
9
|
+
function toComparableMs(view) {
|
|
10
|
+
const y = view.year, m = view.month, d = view.day;
|
|
11
|
+
const y2 = m <= 2 ? y - 1 : y;
|
|
12
|
+
const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);
|
|
13
|
+
const yoe = y2 - era * 400;
|
|
14
|
+
const m2 = m > 2 ? m - 3 : m + 9;
|
|
15
|
+
const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;
|
|
16
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
17
|
+
const days = era * 146097 + doe - 719468;
|
|
18
|
+
const MS_PER_HOUR = 36e5;
|
|
19
|
+
const MS_PER_MINUTE = 6e4;
|
|
20
|
+
const MS_PER_SECOND = 1e3;
|
|
21
|
+
return days * 864e5 + (view.hour ?? 0) * MS_PER_HOUR + (view.minute ?? 0) * MS_PER_MINUTE + (view.second ?? 0) * MS_PER_SECOND + (view.millisecond ?? 0);
|
|
22
|
+
}
|
|
23
|
+
function asDateTimeFieldView(value) {
|
|
24
|
+
const v = asDateFieldView(value);
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
function compare(a, b) {
|
|
28
|
+
const av = asDateTimeFieldView(a);
|
|
29
|
+
const bv = asDateTimeFieldView(b);
|
|
30
|
+
const am = toComparableMs(av);
|
|
31
|
+
const bm = toComparableMs(bv);
|
|
32
|
+
if (am < bm) return -1;
|
|
33
|
+
if (am > bm) return 1;
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
function isEqual(a, b) {
|
|
37
|
+
return compare(a, b) === 0;
|
|
38
|
+
}
|
|
39
|
+
function isBefore(a, b) {
|
|
40
|
+
return compare(a, b) < 0;
|
|
41
|
+
}
|
|
42
|
+
function isAfter(a, b) {
|
|
43
|
+
return compare(a, b) > 0;
|
|
44
|
+
}
|
|
45
|
+
function min(values) {
|
|
46
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
47
|
+
throw new Error("temporal-fmt: min() requires a non-empty array of values.");
|
|
48
|
+
}
|
|
49
|
+
let best = values[0];
|
|
50
|
+
for (let i = 1; i < values.length; i++) {
|
|
51
|
+
if (compare(values[i], best) < 0) best = values[i];
|
|
52
|
+
}
|
|
53
|
+
return best;
|
|
54
|
+
}
|
|
55
|
+
function max(values) {
|
|
56
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
57
|
+
throw new Error("temporal-fmt: max() requires a non-empty array of values.");
|
|
58
|
+
}
|
|
59
|
+
let best = values[0];
|
|
60
|
+
for (let i = 1; i < values.length; i++) {
|
|
61
|
+
if (compare(values[i], best) > 0) best = values[i];
|
|
62
|
+
}
|
|
63
|
+
return best;
|
|
64
|
+
}
|
|
65
|
+
function clamp(value, lo, hi) {
|
|
66
|
+
if (compare(value, lo) < 0) return lo;
|
|
67
|
+
if (compare(value, hi) > 0) return hi;
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
function isBetween(value, lo, hi) {
|
|
71
|
+
return compare(value, lo) >= 0 && compare(value, hi) <= 0;
|
|
72
|
+
}
|
|
73
|
+
function readYearMonthDay(value) {
|
|
74
|
+
const v = asDateFieldView(value);
|
|
75
|
+
return { year: v.year, month: v.month, day: v.day };
|
|
76
|
+
}
|
|
77
|
+
function todayYearMonthDay() {
|
|
78
|
+
const now = /* @__PURE__ */ new Date();
|
|
79
|
+
return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };
|
|
80
|
+
}
|
|
81
|
+
function dateEqualYearMonthDay(a, b) {
|
|
82
|
+
return a.year === b.year && a.month === b.month && a.day === b.day;
|
|
83
|
+
}
|
|
84
|
+
function isSameDay(value, reference) {
|
|
85
|
+
return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));
|
|
86
|
+
}
|
|
87
|
+
function isToday(value) {
|
|
88
|
+
return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());
|
|
89
|
+
}
|
|
90
|
+
function tomorrowYearMonthDay() {
|
|
91
|
+
const d = /* @__PURE__ */ new Date();
|
|
92
|
+
d.setDate(d.getDate() + 1);
|
|
93
|
+
return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };
|
|
94
|
+
}
|
|
95
|
+
function yesterdayYearMonthDay() {
|
|
96
|
+
const d = /* @__PURE__ */ new Date();
|
|
97
|
+
d.setDate(d.getDate() - 1);
|
|
98
|
+
return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };
|
|
99
|
+
}
|
|
100
|
+
function isTomorrow(value) {
|
|
101
|
+
return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());
|
|
102
|
+
}
|
|
103
|
+
function isYesterday(value) {
|
|
104
|
+
return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());
|
|
105
|
+
}
|
|
106
|
+
function sameWeek(yearA, monthA, dayA, yearB, monthB, dayB) {
|
|
107
|
+
const DAYS_PER_400_YEARS = 146097;
|
|
108
|
+
const daysSince = (y, m, d) => {
|
|
109
|
+
const y2 = m <= 2 ? y - 1 : y;
|
|
110
|
+
const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;
|
|
111
|
+
const yoe = y2 - era * 400;
|
|
112
|
+
const m2 = m > 2 ? m - 3 : m + 9;
|
|
113
|
+
const doy = Math.floor(Math.floor(153 * m2 + 2) / 5 + d - 1);
|
|
114
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
115
|
+
return era * DAYS_PER_400_YEARS + doe - 719468;
|
|
116
|
+
};
|
|
117
|
+
const a = daysSince(yearA, monthA, dayA);
|
|
118
|
+
const b = daysSince(yearB, monthB, dayB);
|
|
119
|
+
if (Math.abs(a - b) >= 7) return false;
|
|
120
|
+
const weekdayOf = (y, m, d) => {
|
|
121
|
+
const days = daysSince(y, m, d);
|
|
122
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
123
|
+
const jsDow = date.getUTCDay();
|
|
124
|
+
return jsDow === 0 ? 7 : jsDow;
|
|
125
|
+
};
|
|
126
|
+
const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);
|
|
127
|
+
const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);
|
|
128
|
+
return wa === wb;
|
|
129
|
+
}
|
|
130
|
+
function isSameWeek(value, reference) {
|
|
131
|
+
const a = readYearMonthDay(value);
|
|
132
|
+
const b = readYearMonthDay(reference);
|
|
133
|
+
return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);
|
|
134
|
+
}
|
|
135
|
+
function isSameMonth(value, reference) {
|
|
136
|
+
const a = readYearMonthDay(value);
|
|
137
|
+
const b = readYearMonthDay(reference);
|
|
138
|
+
return a.year === b.year && a.month === b.month;
|
|
139
|
+
}
|
|
140
|
+
function isSameQuarter(value, reference) {
|
|
141
|
+
const a = readYearMonthDay(value);
|
|
142
|
+
const b = readYearMonthDay(reference);
|
|
143
|
+
return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);
|
|
144
|
+
}
|
|
145
|
+
function isSameYear(value, reference) {
|
|
146
|
+
return readYearMonthDay(value).year === readYearMonthDay(reference).year;
|
|
147
|
+
}
|
|
148
|
+
function isWeekend(value) {
|
|
149
|
+
if (typeof value !== "object" || value === null) {
|
|
150
|
+
throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);
|
|
151
|
+
}
|
|
152
|
+
const v = value;
|
|
153
|
+
if (typeof v.dayOfWeek !== "number") {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return v.dayOfWeek === 6 || v.dayOfWeek === 7;
|
|
159
|
+
}
|
|
160
|
+
function isWeekday(value) {
|
|
161
|
+
return !isWeekend(value);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/recurrence.ts
|
|
165
|
+
function recurrence(start, rule) {
|
|
166
|
+
let current = start;
|
|
167
|
+
let count = 0;
|
|
168
|
+
const history = [];
|
|
169
|
+
const MAX_HISTORY = 1e4;
|
|
170
|
+
let atEnd = false;
|
|
171
|
+
let atStart = true;
|
|
172
|
+
function matches(value) {
|
|
173
|
+
const v = value;
|
|
174
|
+
if (rule.byWeekday && rule.byWeekday.length > 0) {
|
|
175
|
+
if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;
|
|
176
|
+
}
|
|
177
|
+
if (rule.byMonthDay && rule.byMonthDay.length > 0) {
|
|
178
|
+
if (!rule.byMonthDay.includes(v.day ?? 0)) return false;
|
|
179
|
+
}
|
|
180
|
+
if (rule.byMonth && rule.byMonth.length > 0) {
|
|
181
|
+
if (!rule.byMonth.includes(v.month ?? 0)) return false;
|
|
182
|
+
}
|
|
183
|
+
if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
function advance(value, steps) {
|
|
187
|
+
const unit = rule.frequency === "secondly" ? "seconds" : rule.frequency === "minutely" ? "minutes" : rule.frequency === "hourly" ? "hours" : rule.frequency === "daily" ? "days" : rule.frequency === "weekly" ? "weeks" : rule.frequency === "monthly" ? "months" : "years";
|
|
188
|
+
return add(value, steps * rule.interval, unit);
|
|
189
|
+
}
|
|
190
|
+
function recordHistory(value) {
|
|
191
|
+
history.push(value);
|
|
192
|
+
if (history.length > MAX_HISTORY) history.shift();
|
|
193
|
+
}
|
|
194
|
+
function nextMatch(value) {
|
|
195
|
+
let candidate = value;
|
|
196
|
+
let safetyCounter = 0;
|
|
197
|
+
do {
|
|
198
|
+
candidate = advance(candidate, 1);
|
|
199
|
+
safetyCounter++;
|
|
200
|
+
if (safetyCounter > 1e3) {
|
|
201
|
+
atEnd = true;
|
|
202
|
+
return { value: candidate, found: false };
|
|
203
|
+
}
|
|
204
|
+
if (rule.until && compare(candidate, rule.until) > 0) {
|
|
205
|
+
atEnd = true;
|
|
206
|
+
return { value: candidate, found: false };
|
|
207
|
+
}
|
|
208
|
+
} while (!matches(candidate));
|
|
209
|
+
return { value: candidate, found: true };
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
next() {
|
|
213
|
+
if (atEnd) return { value: void 0, done: true };
|
|
214
|
+
if (atStart) {
|
|
215
|
+
atStart = false;
|
|
216
|
+
if (matches(current)) {
|
|
217
|
+
count++;
|
|
218
|
+
if (rule.count !== void 0 && count >= rule.count) atEnd = true;
|
|
219
|
+
if (rule.until && compare(current, rule.until) > 0) {
|
|
220
|
+
atEnd = true;
|
|
221
|
+
return { value: void 0, done: true };
|
|
222
|
+
}
|
|
223
|
+
recordHistory(current);
|
|
224
|
+
return { value: current, done: false };
|
|
225
|
+
}
|
|
226
|
+
const advanced2 = nextMatch(current);
|
|
227
|
+
if (!advanced2.found) return { value: void 0, done: true };
|
|
228
|
+
current = advanced2.value;
|
|
229
|
+
count++;
|
|
230
|
+
if (rule.count !== void 0 && count >= rule.count) atEnd = true;
|
|
231
|
+
recordHistory(current);
|
|
232
|
+
return { value: current, done: false };
|
|
233
|
+
}
|
|
234
|
+
const advanced = nextMatch(current);
|
|
235
|
+
if (!advanced.found) return { value: void 0, done: true };
|
|
236
|
+
current = advanced.value;
|
|
237
|
+
count++;
|
|
238
|
+
if (rule.count !== void 0 && count >= rule.count) atEnd = true;
|
|
239
|
+
recordHistory(current);
|
|
240
|
+
return { value: current, done: false };
|
|
241
|
+
},
|
|
242
|
+
previous() {
|
|
243
|
+
if (history.length === 0) return { value: void 0, done: true };
|
|
244
|
+
const v = history.pop();
|
|
245
|
+
return { value: v, done: false };
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function take(iter, n) {
|
|
250
|
+
const result = [];
|
|
251
|
+
for (let i = 0; i < n; i++) {
|
|
252
|
+
const r = iter.next();
|
|
253
|
+
if (r.done) break;
|
|
254
|
+
result.push(r.value);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
var MAX_SKIP_COLLECTION = 1e5;
|
|
259
|
+
function skip(iter, n) {
|
|
260
|
+
for (let i = 0; i < n; i++) {
|
|
261
|
+
const r = iter.next();
|
|
262
|
+
if (r.done) break;
|
|
263
|
+
}
|
|
264
|
+
const result = [];
|
|
265
|
+
while (result.length < MAX_SKIP_COLLECTION) {
|
|
266
|
+
const r = iter.next();
|
|
267
|
+
if (r.done) return result;
|
|
268
|
+
result.push(r.value);
|
|
269
|
+
}
|
|
270
|
+
throw new RangeError(
|
|
271
|
+
`temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still producing \u2014 add a count or until to the rule, or use take(iter, n) for a bounded read.`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
function between(start, rule, rangeStart, rangeEnd) {
|
|
275
|
+
const iter = recurrence(start, rule);
|
|
276
|
+
const result = [];
|
|
277
|
+
while (true) {
|
|
278
|
+
const r = iter.next();
|
|
279
|
+
if (r.done) break;
|
|
280
|
+
if (compare(r.value, rangeEnd) >= 0) break;
|
|
281
|
+
if (compare(r.value, rangeStart) >= 0) result.push(r.value);
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
function parseRRule(input) {
|
|
286
|
+
const parts = input.trim().toUpperCase().replace(/^RRULE:/, "").split(";");
|
|
287
|
+
const rule = { frequency: "daily", interval: 1 };
|
|
288
|
+
const frequencies = /* @__PURE__ */ new Set([
|
|
289
|
+
"SECONDLY",
|
|
290
|
+
"MINUTELY",
|
|
291
|
+
"HOURLY",
|
|
292
|
+
"DAILY",
|
|
293
|
+
"WEEKLY",
|
|
294
|
+
"MONTHLY",
|
|
295
|
+
"YEARLY"
|
|
296
|
+
]);
|
|
297
|
+
for (const part of parts) {
|
|
298
|
+
if (!part) continue;
|
|
299
|
+
const eq = part.indexOf("=");
|
|
300
|
+
if (eq < 0) continue;
|
|
301
|
+
const key = part.slice(0, eq);
|
|
302
|
+
const value = part.slice(eq + 1);
|
|
303
|
+
switch (key) {
|
|
304
|
+
case "FREQ":
|
|
305
|
+
if (!frequencies.has(value)) {
|
|
306
|
+
throw new RangeError(`temporal-fmt: unsupported RRULE frequency "${value}".`);
|
|
307
|
+
}
|
|
308
|
+
rule.frequency = value.toLowerCase();
|
|
309
|
+
break;
|
|
310
|
+
case "INTERVAL": {
|
|
311
|
+
const interval = Number(value);
|
|
312
|
+
if (!Number.isSafeInteger(interval) || interval < 1) {
|
|
313
|
+
throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got "${value}").`);
|
|
314
|
+
}
|
|
315
|
+
rule.interval = interval;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
case "COUNT": {
|
|
319
|
+
const count = Number(value);
|
|
320
|
+
if (!Number.isSafeInteger(count) || count < 1) {
|
|
321
|
+
throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got "${value}").`);
|
|
322
|
+
}
|
|
323
|
+
rule.count = count;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
case "UNTIL":
|
|
327
|
+
rule.until = value;
|
|
328
|
+
break;
|
|
329
|
+
case "BYDAY":
|
|
330
|
+
rule.byWeekday = value.split(",").map((d) => {
|
|
331
|
+
const m = d.match(/^([+-]?\d)?([A-Z]{2})$/);
|
|
332
|
+
if (!m) return 0;
|
|
333
|
+
const wd = m[2];
|
|
334
|
+
const map = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };
|
|
335
|
+
return map[wd] ?? 0;
|
|
336
|
+
});
|
|
337
|
+
break;
|
|
338
|
+
case "BYMONTHDAY": {
|
|
339
|
+
const values = value.split(",").map(Number);
|
|
340
|
+
if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {
|
|
341
|
+
throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);
|
|
342
|
+
}
|
|
343
|
+
rule.byMonthDay = values;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case "BYMONTH": {
|
|
347
|
+
const values = value.split(",").map(Number);
|
|
348
|
+
if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {
|
|
349
|
+
throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);
|
|
350
|
+
}
|
|
351
|
+
rule.byMonth = values;
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return rule;
|
|
357
|
+
}
|
|
358
|
+
function formatRRule(rule) {
|
|
359
|
+
const parts = [`FREQ=${rule.frequency.toUpperCase()}`];
|
|
360
|
+
if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);
|
|
361
|
+
if (rule.count !== void 0) parts.push(`COUNT=${rule.count}`);
|
|
362
|
+
if (rule.until !== void 0) parts.push(`UNTIL=${String(rule.until)}`);
|
|
363
|
+
if (rule.byWeekday && rule.byWeekday.length > 0) {
|
|
364
|
+
const map = { 1: "MO", 2: "TU", 3: "WE", 4: "TH", 5: "FR", 6: "SA", 7: "SU" };
|
|
365
|
+
parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(",")}`);
|
|
366
|
+
}
|
|
367
|
+
if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(",")}`);
|
|
368
|
+
if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(",")}`);
|
|
369
|
+
return parts.join(";");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export {
|
|
373
|
+
compare,
|
|
374
|
+
isEqual,
|
|
375
|
+
isBefore,
|
|
376
|
+
isAfter,
|
|
377
|
+
min,
|
|
378
|
+
max,
|
|
379
|
+
clamp,
|
|
380
|
+
isBetween,
|
|
381
|
+
isSameDay,
|
|
382
|
+
isToday,
|
|
383
|
+
isTomorrow,
|
|
384
|
+
isYesterday,
|
|
385
|
+
isSameWeek,
|
|
386
|
+
isSameMonth,
|
|
387
|
+
isSameQuarter,
|
|
388
|
+
isSameYear,
|
|
389
|
+
isWeekend,
|
|
390
|
+
isWeekday,
|
|
391
|
+
recurrence,
|
|
392
|
+
take,
|
|
393
|
+
skip,
|
|
394
|
+
between,
|
|
395
|
+
parseRRule,
|
|
396
|
+
formatRRule
|
|
397
|
+
};
|
|
398
|
+
//# sourceMappingURL=chunk-VNIUIQA4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/comparison.ts","../src/recurrence.ts"],"sourcesContent":["// Comparison helpers. Pure functions over the field\n// shape, no Temporal namespace needed — same convention as\n// calendarUtils.ts. All comparisons are field-based, not identity-based,\n// so a polyfill PlainDate compares equal to a native PlainDate with the\n// same year/month/day.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction toComparableMs(view: DateTimeFieldView): number {\n // Exact proleptic-Gregorian day count (Howard Hinnant's days_from_civil,\n // same algorithm arithmetic.ts/interval.ts use) plus the time-of-day —\n // NOT an approximation. The previous implementation multiplied the\n // year offset by an average 365.2425 days/year, on the theory that the\n // error \"is constant for any given year, so it cancels out in a diff\".\n // That's only true when both dates fall in the same year: the year\n // term's error differs by up to ~0.76 days between adjacent years\n // (365.2425 vs 365/366 actual), which corrupts cross-year comparisons\n // near the boundary — e.g. isAfter(2025-01-01T00:00, 2024-12-31T18:12)\n // returned false (truth: ~5.8h after), and isEqual() returned true for\n // instants ~18h apart (2025-01-01T00:00 vs 2024-12-31T05:49:12).\n // Exact arithmetic has no such error term.\n const y = view.year!, m = view.month!, d = view.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n return days * 86_400_000\n + (view.hour ?? 0) * MS_PER_HOUR\n + (view.minute ?? 0) * MS_PER_MINUTE\n + (view.second ?? 0) * MS_PER_SECOND\n + (view.millisecond ?? 0);\n}\n\nfunction asDateTimeFieldView(value: unknown): DateTimeFieldView {\n const v = asDateFieldView(value);\n return v as DateTimeFieldView;\n}\n\n// Returns < 0 if a < b, 0 if a === b, > 0 if a > b. Same convention as\n// Array.prototype.sort and Temporal.PlainDate.compare. Lets callers\n// pass this directly as a comparator.\nexport function compare(a: unknown, b: unknown): number {\n const av = asDateTimeFieldView(a);\n const bv = asDateTimeFieldView(b);\n const am = toComparableMs(av);\n const bm = toComparableMs(bv);\n if (am < bm) return -1;\n if (am > bm) return 1;\n return 0;\n}\n\nexport function isEqual(a: unknown, b: unknown): boolean {\n return compare(a, b) === 0;\n}\n\nexport function isBefore(a: unknown, b: unknown): boolean {\n return compare(a, b) < 0;\n}\n\nexport function isAfter(a: unknown, b: unknown): boolean {\n return compare(a, b) > 0;\n}\n\nexport function min(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: min() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) < 0) best = values[i];\n }\n return best;\n}\n\nexport function max(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: max() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) > 0) best = values[i];\n }\n return best;\n}\n\nexport function clamp(value: unknown, lo: unknown, hi: unknown): unknown {\n if (compare(value, lo) < 0) return lo;\n if (compare(value, hi) > 0) return hi;\n return value;\n}\n\nexport function isBetween(value: unknown, lo: unknown, hi: unknown): boolean {\n return compare(value, lo) >= 0 && compare(value, hi) <= 0;\n}\n\n// Semantic helpers — same-day/week/month/quarter/year tests. Useful for\n// UI (\"is this event today?\") and for grouping/aggregation. All take a\n// reference value to compare against; the \"isToday\" / \"isTomorrow\" /\n// \"isYesterday\" variants compute the reference from the current system\n// date themselves so callers don't have to.\nfunction readYearMonthDay(value: unknown): { year: number; month: number; day: number } {\n const v = asDateFieldView(value);\n return { year: v.year!, month: v.month!, day: v.day! };\n}\n\nfunction todayYearMonthDay(): { year: number; month: number; day: number } {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n}\n\nfunction dateEqualYearMonthDay(a: { year: number; month: number; day: number }, b: { year: number; month: number; day: number }): boolean {\n return a.year === b.year && a.month === b.month && a.day === b.day;\n}\n\nexport function isSameDay(value: unknown, reference: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));\n}\n\nexport function isToday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());\n}\n\nfunction tomorrowYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() + 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nfunction yesterdayYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() - 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nexport function isTomorrow(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());\n}\n\nexport function isYesterday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());\n}\n\nfunction sameWeek(yearA: number, monthA: number, dayA: number, yearB: number, monthB: number, dayB: number): boolean {\n // Same week iff the dates are within 7 days of each other AND the earlier\n // one's weekday is ≤ the later one's weekday (so we don't say \"Sun and\n // the following Mon are in the same week\" — ISO weeks run Mon-Sun).\n // Easier: compute ISO week numbers via isoWeekYearAndWeek, compare both\n // year and week. That handles the year-boundary case (Dec 31 vs Jan 1 of\n // the next year, which can be in the same ISO week).\n // Local import to avoid module-cycle: isoWeek.ts doesn't import from here.\n // Lazy require-style dynamic via inline static import at top would be\n // cleaner; this inline computation is fine for a pure helper.\n // ...actually let's just compute weekday offsets directly. Same-ISO-week\n // iff |d1 - d2| < 7 days AND the earlier date's ISO weekday ≤ the later\n // date's ISO weekday so they fall in the same Mon-Sun span.\n const DAYS_PER_400_YEARS = 146097;\n const daysSince = (y: number, m: number, d: number): number => {\n // Zeller's congruence variant — convert Y/M/D to a day count.\n // Using a simple algorithm here that's correct for proleptic Gregorian.\n const y2 = m <= 2 ? y - 1 : y;\n const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((Math.floor(153 * m2 + 2) / 5) + d - 1);\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * DAYS_PER_400_YEARS + doe - 719468;\n };\n const a = daysSince(yearA, monthA, dayA);\n const b = daysSince(yearB, monthB, dayB);\n if (Math.abs(a - b) >= 7) return false;\n // Same week iff both dates round to the same Monday. Compute the\n // Monday-of-week for each: subtract (weekday - 1) days, where weekday\n // is 1=Mon..7=Sun. Use the standard Zeller formula for weekday.\n const weekdayOf = (y: number, m: number, d: number): number => {\n const days = daysSince(y, m, d);\n // 1970-01-01 was a Thursday (ISO 4). So weekday = ((days + 3) mod 7) + 1\n // mapped to Mon=1..Sun=7. Let's compute: Sun=0..Sat=6 via JS Date.\n const date = new Date(Date.UTC(y, m - 1, d));\n const jsDow = date.getUTCDay(); // 0=Sun..6=Sat\n return jsDow === 0 ? 7 : jsDow;\n };\n const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);\n const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);\n return wa === wb;\n}\n\nexport function isSameWeek(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);\n}\n\nexport function isSameMonth(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && a.month === b.month;\n}\n\nexport function isSameQuarter(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);\n}\n\nexport function isSameYear(value: unknown, reference: unknown): boolean {\n return readYearMonthDay(value).year === readYearMonthDay(reference).year;\n}\n\n// isWeekend / isWeekday depend on dayOfWeek — present on PlainDate,\n// PlainDateTime, ZonedDateTime, but NOT on PlainTime. Callers passing\n// PlainTime get a descriptive throw. Doesn't call asDateFieldView\n// because that would reject PlainTime (no year/month/day) before\n// reaching the dayOfWeek check — and the error we want here is the\n// \"no dayOfWeek field\" one, not the \"no date fields\" one.\nexport function isWeekend(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);\n }\n const v = value as { dayOfWeek?: unknown };\n if (typeof v.dayOfWeek !== 'number') {\n throw new Error(\n `temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`\n );\n }\n // 6=Sat, 7=Sun in Temporal's Mon=1..Sun=7 numbering.\n return v.dayOfWeek === 6 || v.dayOfWeek === 7;\n}\n\nexport function isWeekday(value: unknown): boolean {\n return !isWeekend(value);\n}\n","// Recurrence engine. Deterministic RRULE-like\n// recurrence without pulling in a runtime dependency. Supports\n// secondly/minutely/hourly/daily/weekly/monthly/yearly frequencies,\n// interval, count, until, weekdays, monthDays, positional rules,\n// exclusions, inclusions.\n//\n// RRULE interop: parseRRule() / formatRRule() convert to/from the\n// standard iCalendar RRULE string format (RFC 5545). Implemented\n// without depending on a RRULE library — the grammar is small enough\n// to handle inline.\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport type RecurrenceFrequency = 'secondly' | 'minutely' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';\n\nexport interface RecurrenceRule {\n frequency: RecurrenceFrequency;\n interval: number; // default 1\n count?: number; // max occurrences\n until?: unknown; // Temporal value — recurrence ends before this\n // For weekly: ISO weekdays to include (1=Mon..7=Sun). Empty = all.\n byWeekday?: number[];\n // For monthly/yearly: days of month to include (1-31, or negative\n // for end-of-month: -1 = last day).\n byMonthDay?: number[];\n // For yearly: months to include (1-12).\n byMonth?: number[];\n // For weekly: which week of the year (1-53).\n byWeek?: number[];\n // Exclusions: dates to skip even if they match the rule.\n exDates?: unknown[];\n // Inclusions: dates to add even if they don't match the rule.\n rDates?: unknown[];\n}\n\nexport interface RecurrenceIterator {\n next(): { value: unknown; done: boolean } | { value: undefined; done: true };\n previous(): { value: unknown; done: boolean } | { value: undefined; done: true };\n}\n\n// Creates a recurrence iterator starting from `start`. The first call\n// to next() returns `start` itself (if it matches the rule); subsequent\n// calls return the next matching occurrence.\nexport function recurrence(start: unknown, rule: RecurrenceRule): RecurrenceIterator {\n let current: unknown = start;\n let count = 0;\n // Track past values for previous() — keeps a ring buffer of the last\n // N occurrences so previous() can walk back without recomputing.\n const history: unknown[] = [];\n const MAX_HISTORY = 10_000;\n let atEnd = false;\n let atStart = true;\n\n function matches(value: unknown): boolean {\n const v = value as { dayOfWeek?: number; day?: number; month?: number; year?: number; hour?: number; minute?: number; second?: number };\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) {\n if (!rule.byMonthDay.includes(v.day ?? 0)) return false;\n }\n if (rule.byMonth && rule.byMonth.length > 0) {\n if (!rule.byMonth.includes(v.month ?? 0)) return false;\n }\n if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;\n return true;\n }\n\n function advance(value: unknown, steps: number): unknown {\n // Add `steps` * interval units to `value`.\n const unit = rule.frequency === 'secondly' ? 'seconds'\n : rule.frequency === 'minutely' ? 'minutes'\n : rule.frequency === 'hourly' ? 'hours'\n : rule.frequency === 'daily' ? 'days'\n : rule.frequency === 'weekly' ? 'weeks'\n : rule.frequency === 'monthly' ? 'months'\n : 'years';\n return add(value, steps * rule.interval, unit as Parameters<typeof add>[2]);\n }\n\n function recordHistory(value: unknown): void {\n history.push(value);\n if (history.length > MAX_HISTORY) history.shift();\n }\n\n function nextMatch(value: unknown): { value: unknown; found: boolean } {\n // Advance one step at a time until we find a match. For\n // byWeekday/byMonthDay rules this can skip multiple steps.\n let candidate = value;\n let safetyCounter = 0;\n do {\n candidate = advance(candidate, 1);\n safetyCounter++;\n if (safetyCounter > 1000) {\n // Defensive — if the rule is so restrictive no match exists\n // within 1000 steps, give up rather than spin forever. The\n // caller must treat this as \"no more occurrences\", not as a\n // real match — returning the unmatched candidate here used to\n // get handed back to next()'s caller as if it were valid.\n atEnd = true;\n return { value: candidate, found: false };\n }\n if (rule.until && compare(candidate, rule.until) > 0) {\n atEnd = true;\n return { value: candidate, found: false };\n }\n } while (!matches(candidate));\n return { value: candidate, found: true };\n }\n\n return {\n next() {\n if (atEnd) return { value: undefined, done: true };\n if (atStart) {\n atStart = false;\n if (matches(current)) {\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n if (rule.until && compare(current, rule.until) > 0) {\n atEnd = true;\n return { value: undefined, done: true };\n }\n // Include rDates that match the current value.\n recordHistory(current);\n return { value: current, done: false };\n }\n // If start doesn't match, advance to first match.\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n }\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n },\n previous() {\n if (history.length === 0) return { value: undefined, done: true };\n const v = history.pop()!;\n return { value: v, done: false };\n },\n };\n}\n\n// Take N occurrences from a recurrence iterator.\nexport function take(iter: RecurrenceIterator, n: number): unknown[] {\n const result: unknown[] = [];\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n result.push(r.value);\n }\n return result;\n}\n\n// Upper bound on how many occurrences skip() will collect after its\n// skip phase. skip() used to call take(iter, Number.MAX_SAFE_INTEGER),\n// and an iterator over an unbounded rule (no count, no until — e.g.\n// { frequency: 'daily', interval: 1 }) never returns done: next() always\n// has another day. The call then looped forever, pushing into a result\n// array until the process OOM'd. Same cap style as businessCalendar.ts.\nconst MAX_SKIP_COLLECTION = 100_000;\n\n// Skip N occurrences from a recurrence iterator, returning the ones\n// that follow. Throws a RangeError when the iterator is still producing\n// occurrences after MAX_SKIP_COLLECTION — that means the rule is\n// unbounded (no count/until) and \"everything after the skip\" has no\n// finite answer.\nexport function skip(iter: RecurrenceIterator, n: number): unknown[] {\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n }\n const result: unknown[] = [];\n while (result.length < MAX_SKIP_COLLECTION) {\n const r = iter.next();\n if (r.done) return result;\n result.push(r.value);\n }\n throw new RangeError(\n `temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still ` +\n `producing — add a count or until to the rule, or use take(iter, n) for a bounded read.`\n );\n}\n\n// All occurrences between two dates (inclusive of start, exclusive of end).\nexport function between(start: unknown, rule: RecurrenceRule, rangeStart: unknown, rangeEnd: unknown): unknown[] {\n const iter = recurrence(start, rule);\n const result: unknown[] = [];\n while (true) {\n const r = iter.next();\n if (r.done) break;\n if (compare(r.value, rangeEnd) >= 0) break;\n if (compare(r.value, rangeStart) >= 0) result.push(r.value);\n }\n return result;\n}\n\n// Parses an RRULE string like \"FREQ=DAILY;INTERVAL=2;COUNT=5\" into a\n// RecurrenceRule. Doesn't support every RRULE feature (BYSETPOS,\n// BYHOUR, BYMINUTE, BYSECOND are parsed but not enforced by the\n// recurrence iterator above), but covers the common cases.\nexport function parseRRule(input: string): RecurrenceRule {\n const parts = input.trim().toUpperCase().replace(/^RRULE:/, '').split(';');\n const rule: RecurrenceRule = { frequency: 'daily', interval: 1 };\n const frequencies: ReadonlySet<string> = new Set([\n 'SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY',\n ]);\n for (const part of parts) {\n if (!part) continue;\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n switch (key) {\n case 'FREQ':\n if (!frequencies.has(value)) {\n throw new RangeError(`temporal-fmt: unsupported RRULE frequency \"${value}\".`);\n }\n rule.frequency = value.toLowerCase() as RecurrenceFrequency;\n break;\n case 'INTERVAL': {\n const interval = Number(value);\n if (!Number.isSafeInteger(interval) || interval < 1) {\n throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got \"${value}\").`);\n }\n rule.interval = interval;\n break;\n }\n case 'COUNT': {\n const count = Number(value);\n if (!Number.isSafeInteger(count) || count < 1) {\n throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got \"${value}\").`);\n }\n rule.count = count;\n break;\n }\n case 'UNTIL':\n rule.until = value;\n break;\n case 'BYDAY':\n rule.byWeekday = value.split(',').map((d) => {\n const m = d.match(/^([+-]?\\d)?([A-Z]{2})$/);\n if (!m) return 0;\n const wd = m[2];\n const map: Record<string, number> = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };\n return map[wd!] ?? 0;\n });\n break;\n case 'BYMONTHDAY': {\n const values = value.split(',').map(Number);\n if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);\n }\n rule.byMonthDay = values;\n break;\n }\n case 'BYMONTH': {\n const values = value.split(',').map(Number);\n if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);\n }\n rule.byMonth = values;\n break;\n }\n }\n }\n return rule;\n}\n\nexport function formatRRule(rule: RecurrenceRule): string {\n const parts: string[] = [`FREQ=${rule.frequency.toUpperCase()}`];\n if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);\n if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);\n if (rule.until !== undefined) parts.push(`UNTIL=${String(rule.until)}`);\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n const map: Record<number, string> = { 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' };\n parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(',')}`);\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(',')}`);\n if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(',')}`);\n return parts.join(';');\n}"],"mappings":"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","names":["advanced"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["// format strings are basically always short hand-written stuff like \"yyyy-MM-dd\"\n// so if something's way longer than that, cap it before tokenize() chokes on it\nexport const MAX_FORMAT_LENGTH = 1000;\nexport const MAX_INPUT_LENGTH = 100_000;"],"mappings":";AAEO,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/temporalProvider.ts
|
|
2
|
+
var injectedTemporal;
|
|
3
|
+
var onTemporalChanged = [];
|
|
4
|
+
function subscribeToTemporalChanges(listener) {
|
|
5
|
+
onTemporalChanged.push(listener);
|
|
6
|
+
}
|
|
7
|
+
function setTemporal(temporal) {
|
|
8
|
+
injectedTemporal = temporal;
|
|
9
|
+
for (const listener of onTemporalChanged) listener();
|
|
10
|
+
}
|
|
11
|
+
function resolveTemporal() {
|
|
12
|
+
return injectedTemporal ?? globalThis.Temporal;
|
|
13
|
+
}
|
|
14
|
+
function getTemporal() {
|
|
15
|
+
const temporal = resolveTemporal();
|
|
16
|
+
if (!temporal) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"temporal-fmt: parse() needs a Temporal implementation to construct its result. Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal (native on Node 26+, or a polyfill like temporal-polyfill)."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return temporal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
subscribeToTemporalChanges,
|
|
26
|
+
setTemporal,
|
|
27
|
+
getTemporal
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-ZQZF5EXW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/temporalProvider.ts"],"sourcesContent":["// tsconfig here is lib: [\"ESNext\"] only, so there's no ambient Temporal\n// namespace type to lean on. everywhere else in the codebase we just read\n// fields off whatever Temporal-like object gets passed in (see TemporalLike\n// in tokens.ts) but this file is the one place that actually touches the\n// Temporal namespace itself — PlainDate.from and friends. parse() uses it\n// to build results, tokens.ts uses it to check native Intl<->Temporal\n// support. you can hand us your own implementation via setTemporal(), or\n// we just grab globalThis.Temporal if there is one\ninterface TemporalFactory {\n from(fields: Record<string, number | string | undefined>, options?: { overflow?: 'constrain' | 'reject'; disambiguation?: 'compatible' | 'earlier' | 'later' | 'reject'; offset?: 'use' | 'ignore' | 'prefer' | 'reject' }): unknown;\n // compare() is on the namespace, not instances (Temporal.PlainDate.compare).\n // made optional since someone might hand us a stripped-down shim that\n // doesn't bother implementing it\n compare?(one: unknown, two: unknown): number;\n}\n\n// Instant doesn't use from(fields) like the others, it's\n// fromEpochMilliseconds / fromEpochNanoseconds. needs its own shape.\ninterface InstantFactory {\n from(iso: string): unknown;\n fromEpochMilliseconds(ms: number): unknown;\n fromEpochMicroseconds(µs: number): unknown;\n fromEpochNanoseconds(ns: bigint): unknown;\n}\n\n// Duration uses from(fields) too, but also has round() and compare()\ninterface DurationFactory extends TemporalFactory {\n compare?(one: unknown, two: unknown): number;\n prototype?: { round(options: unknown): unknown };\n}\n\n// PlainYearMonth / PlainMonthDay also just use from(fields) — same shape\n// as TemporalFactory really, kept as its own type so callers can narrow\nexport interface TemporalNamespace {\n PlainDate: TemporalFactory;\n PlainTime: TemporalFactory;\n PlainDateTime: TemporalFactory;\n ZonedDateTime: TemporalFactory;\n Instant?: InstantFactory;\n Duration?: DurationFactory;\n PlainYearMonth?: TemporalFactory;\n PlainMonthDay?: TemporalFactory;\n}\n\nlet injectedTemporal: TemporalNamespace | undefined;\n\n// anything caching something tied to WHICH Temporal impl is active\n// (right now just tokens.ts's native-Intl probe) registers a listener\n// here so it knows to invalidate when setTemporal() swaps things out —\n// see setTemporal() below for why that matters. kept this as a plain\n// array instead of a full event emitter since all we need is \"call\n// everyone back, in order, no payload\" — didn't need unsubscribe either\nconst onTemporalChanged: Array<() => void> = [];\n\nexport function subscribeToTemporalChanges(listener: () => void): void {\n onTemporalChanged.push(listener);\n}\n\n/**\n * Explicitly hand temporal-fmt the Temporal implementation to use, instead\n * of relying on a global `Temporal`. Call this once, before your first\n * `format()`/`parse()`/`parseISO()`/etc.\n *\n * Call with no argument (or `undefined`) to clear the override and fall\n * back to `globalThis.Temporal` again.\n *\n * @example\n * import { Temporal } from 'temporal-polyfill';\n * import { setTemporal } from 'temporal-fmt';\n * setTemporal(Temporal);\n */\nexport function setTemporal(temporal?: TemporalNamespace): void {\n injectedTemporal = temporal;\n // tokens.ts caches its native-Intl probe result based on whatever\n // Temporal was active the first time it ran. if that changes later\n // (native -> polyfill, say) the cached result goes stale. so just reset\n // everyone on any setTemporal() call — costs one extra probe re-run,\n // worth it to not silently use a stale answer\n for (const listener of onTemporalChanged) listener();\n}\n\nfunction resolveTemporal(): TemporalNamespace | undefined {\n return injectedTemporal ?? (globalThis as unknown as { Temporal?: TemporalNamespace }).Temporal;\n}\n\nexport function getTemporal(): TemporalNamespace {\n const temporal = resolveTemporal();\n if (!temporal) {\n throw new Error(\n 'temporal-fmt: parse() needs a Temporal implementation to construct its result. ' +\n 'Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal ' +\n '(native on Node 26+, or a polyfill like temporal-polyfill).'\n );\n }\n return temporal;\n}\n"],"mappings":";AA4CA,IAAI;AAQJ,IAAM,oBAAuC,CAAC;AAEvC,SAAS,2BAA2B,UAA4B;AACrE,oBAAkB,KAAK,QAAQ;AACjC;AAeO,SAAS,YAAY,UAAoC;AAC9D,qBAAmB;AAMnB,aAAW,YAAY,kBAAmB,UAAS;AACrD;AAEA,SAAS,kBAAiD;AACxD,SAAO,oBAAqB,WAA2D;AACzF;AAEO,SAAS,cAAiC;AAC/C,QAAM,WAAW,gBAAgB;AACjC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
package/dist/config.d.cts
CHANGED
|
@@ -13,10 +13,12 @@ export interface TemporalFmtConfig {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const DEFAULT_CONFIG: TemporalFmtConfig;
|
|
15
15
|
export declare function createConfig(overrides?: Partial<TemporalFmtConfig>): Readonly<TemporalFmtConfig>;
|
|
16
|
-
|
|
16
|
+
interface ConfigOverrides {
|
|
17
17
|
locale?: string;
|
|
18
18
|
calendar?: string;
|
|
19
19
|
timezone?: string;
|
|
20
20
|
numberingSystem?: string;
|
|
21
21
|
lenient?: boolean;
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
|
+
export declare function mergeWithConfig<T extends Record<string, unknown>>(config: Readonly<TemporalFmtConfig> | undefined, perCall: T): Record<string, unknown> & ConfigOverrides;
|
|
24
|
+
export {};
|
package/dist/config.d.ts
CHANGED
|
@@ -13,10 +13,12 @@ export interface TemporalFmtConfig {
|
|
|
13
13
|
}
|
|
14
14
|
export declare const DEFAULT_CONFIG: TemporalFmtConfig;
|
|
15
15
|
export declare function createConfig(overrides?: Partial<TemporalFmtConfig>): Readonly<TemporalFmtConfig>;
|
|
16
|
-
|
|
16
|
+
interface ConfigOverrides {
|
|
17
17
|
locale?: string;
|
|
18
18
|
calendar?: string;
|
|
19
19
|
timezone?: string;
|
|
20
20
|
numberingSystem?: string;
|
|
21
21
|
lenient?: boolean;
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
|
+
export declare function mergeWithConfig<T extends Record<string, unknown>>(config: Readonly<TemporalFmtConfig> | undefined, perCall: T): Record<string, unknown> & ConfigOverrides;
|
|
24
|
+
export {};
|