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.
Files changed (103) hide show
  1. package/README.md +480 -9
  2. package/dist/calendarUtils.cjs +259 -4
  3. package/dist/calendarUtils.cjs.map +1 -1
  4. package/dist/calendarUtils.js +33 -1
  5. package/dist/chunk-4N3WCGEY.js +183 -0
  6. package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
  7. package/dist/chunk-5UTV5VP6.js +984 -0
  8. package/dist/chunk-5UTV5VP6.js.map +1 -0
  9. package/dist/chunk-ACKKWH5Z.js +578 -0
  10. package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
  11. package/dist/chunk-EGUUO6LE.js +439 -0
  12. package/dist/chunk-EGUUO6LE.js.map +1 -0
  13. package/dist/chunk-FBPHNRVW.js +2939 -0
  14. package/dist/chunk-FBPHNRVW.js.map +1 -0
  15. package/dist/chunk-GPB4JHGI.js +65 -0
  16. package/dist/chunk-GPB4JHGI.js.map +1 -0
  17. package/dist/chunk-H7YQX25T.js +170 -0
  18. package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
  19. package/dist/chunk-HXY2GUUM.js +182 -0
  20. package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
  21. package/dist/chunk-L24NPTAJ.js +328 -0
  22. package/dist/chunk-L24NPTAJ.js.map +1 -0
  23. package/dist/chunk-M3GDTHMT.js +169 -0
  24. package/dist/chunk-M3GDTHMT.js.map +1 -0
  25. package/dist/chunk-MKPDLQKD.js +51 -0
  26. package/dist/chunk-MKPDLQKD.js.map +1 -0
  27. package/dist/chunk-TSLPIURC.js +242 -0
  28. package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
  29. package/dist/chunk-UOPAV3WX.js +75 -0
  30. package/dist/chunk-UOPAV3WX.js.map +1 -0
  31. package/dist/chunk-VNIUIQA4.js +398 -0
  32. package/dist/chunk-VNIUIQA4.js.map +1 -0
  33. package/dist/chunk-YEUASGDK.js +9 -0
  34. package/dist/chunk-YEUASGDK.js.map +1 -0
  35. package/dist/chunk-ZQZF5EXW.js +29 -0
  36. package/dist/chunk-ZQZF5EXW.js.map +1 -0
  37. package/dist/config.d.cts +4 -2
  38. package/dist/config.d.ts +4 -2
  39. package/dist/duration.cjs +638 -26
  40. package/dist/duration.cjs.map +1 -1
  41. package/dist/duration.js +28 -1
  42. package/dist/format.cjs +833 -35
  43. package/dist/format.cjs.map +1 -1
  44. package/dist/format.js +20 -1
  45. package/dist/index.cjs +7698 -163
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +108 -24
  48. package/dist/index.d.ts +108 -24
  49. package/dist/index.js +1536 -58
  50. package/dist/index.js.map +1 -1
  51. package/dist/interval.cjs +5480 -35
  52. package/dist/interval.cjs.map +1 -1
  53. package/dist/interval.js +46 -1
  54. package/dist/localeRegistry.cjs +375 -32
  55. package/dist/localeRegistry.cjs.map +1 -1
  56. package/dist/localeRegistry.js +11 -1
  57. package/dist/modApi.d.cts +131 -0
  58. package/dist/modApi.d.ts +131 -0
  59. package/dist/parse.cjs +1797 -70
  60. package/dist/parse.cjs.map +1 -1
  61. package/dist/parse.js +20 -1
  62. package/dist/pattern.d.cts +1 -0
  63. package/dist/pattern.d.ts +1 -0
  64. package/dist/recurrence.cjs +452 -20
  65. package/dist/recurrence.cjs.map +1 -1
  66. package/dist/recurrence.js +19 -1
  67. package/dist/relativeTime.cjs +268 -21
  68. package/dist/relativeTime.cjs.map +1 -1
  69. package/dist/relativeTime.js +14 -1
  70. package/dist/runtime.d.cts +201 -0
  71. package/dist/runtime.d.ts +201 -0
  72. package/dist/timezone.cjs +387 -20
  73. package/dist/timezone.cjs.map +1 -1
  74. package/dist/timezone.js +26 -1
  75. package/package.json +4 -6
  76. package/scripts/cli.mjs +27 -5
  77. package/dist/chunk-2JHEV7ZF.js +0 -7
  78. package/dist/chunk-3MZLTVP3.js +0 -2
  79. package/dist/chunk-3MZLTVP3.js.map +0 -1
  80. package/dist/chunk-4JWGUR4O.js +0 -11
  81. package/dist/chunk-5U5WJ465.js +0 -36
  82. package/dist/chunk-5U5WJ465.js.map +0 -1
  83. package/dist/chunk-C5YESWFT.js +0 -2
  84. package/dist/chunk-C5YESWFT.js.map +0 -1
  85. package/dist/chunk-C6ZZ62ET.js +0 -2
  86. package/dist/chunk-C6ZZ62ET.js.map +0 -1
  87. package/dist/chunk-EHLRZULM.js +0 -5
  88. package/dist/chunk-F4RGUDA3.js +0 -2
  89. package/dist/chunk-F4RGUDA3.js.map +0 -1
  90. package/dist/chunk-J2AQNXF2.js +0 -2
  91. package/dist/chunk-NBXF7V5B.js +0 -2
  92. package/dist/chunk-NBXF7V5B.js.map +0 -1
  93. package/dist/chunk-R52YOKI3.js +0 -7
  94. package/dist/chunk-R52YOKI3.js.map +0 -1
  95. package/dist/chunk-UJR6DHXY.js +0 -2
  96. package/dist/chunk-UJR6DHXY.js.map +0 -1
  97. package/dist/chunk-VQXUMFB2.js +0 -21
  98. package/dist/chunk-XH46JNPK.js +0 -2
  99. package/dist/chunk-XH46JNPK.js.map +0 -1
  100. package/dist/chunk-YB6YAG7E.js +0 -35
  101. package/dist/chunk-YB6YAG7E.js.map +0 -1
  102. package/dist/chunk-ZGMTY52O.js +0 -2
  103. package/dist/chunk-ZGMTY52O.js.map +0 -1
@@ -0,0 +1,578 @@
1
+ import {
2
+ MAX_FORMAT_LENGTH
3
+ } from "./chunk-YEUASGDK.js";
4
+ import {
5
+ FormatSyntaxError,
6
+ InvalidDurationError,
7
+ InvalidLocaleError,
8
+ canonicalCacheKey,
9
+ normalizeLocaleTag
10
+ } from "./chunk-EGUUO6LE.js";
11
+ import {
12
+ asDateFieldView
13
+ } from "./chunk-HXY2GUUM.js";
14
+
15
+ // src/rounding.ts
16
+ var MS_PER_UNIT = {
17
+ day: 864e5,
18
+ hour: 36e5,
19
+ minute: 6e4,
20
+ second: 1e3,
21
+ millisecond: 1
22
+ };
23
+ function applyMode(ms, mode, increment) {
24
+ const sign = ms < 0 ? -1 : 1;
25
+ const abs = Math.abs(ms);
26
+ const stepped = abs / increment;
27
+ let rounded;
28
+ switch (mode) {
29
+ case "nearest":
30
+ rounded = Math.round(stepped);
31
+ break;
32
+ case "floor":
33
+ rounded = Math.floor(stepped);
34
+ break;
35
+ case "ceil":
36
+ rounded = Math.ceil(stepped);
37
+ break;
38
+ case "trunc":
39
+ rounded = Math.trunc(stepped);
40
+ break;
41
+ }
42
+ return sign * rounded * increment;
43
+ }
44
+ function toMs(v) {
45
+ const y = v.year, m = v.month, d = v.day;
46
+ const y2 = m <= 2 ? y - 1 : y;
47
+ const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);
48
+ const yoe = y2 - era * 400;
49
+ const m2 = m > 2 ? m - 3 : m + 9;
50
+ const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;
51
+ const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
52
+ const days = era * 146097 + doe - 719468;
53
+ return days * MS_PER_UNIT.day + (v.hour ?? 0) * MS_PER_UNIT.hour + (v.minute ?? 0) * MS_PER_UNIT.minute + (v.second ?? 0) * MS_PER_UNIT.second + (v.millisecond ?? 0);
54
+ }
55
+ function fromMs(ms, base) {
56
+ const MS_PER_DAY = MS_PER_UNIT.day;
57
+ const totalDays = Math.floor(ms / MS_PER_DAY);
58
+ let withinDay = ms - totalDays * MS_PER_DAY;
59
+ if (withinDay < 0) withinDay += MS_PER_DAY;
60
+ const hour = Math.floor(withinDay / MS_PER_UNIT.hour);
61
+ const minute = Math.floor(withinDay % MS_PER_UNIT.hour / MS_PER_UNIT.minute);
62
+ const second = Math.floor(withinDay % MS_PER_UNIT.minute / MS_PER_UNIT.second);
63
+ const millisecond = withinDay % MS_PER_UNIT.second;
64
+ const z = totalDays + 719468;
65
+ const era = Math.floor((z >= 0 ? z : z - 146096) / 146097);
66
+ const doe = z - era * 146097;
67
+ const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
68
+ const y2 = yoe + era * 400;
69
+ const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
70
+ const mp = Math.floor((5 * doy + 2) / 153);
71
+ const d = doy - Math.floor((153 * mp + 2) / 5) + 1;
72
+ const m = mp < 10 ? mp + 3 : mp - 9;
73
+ const year = m <= 2 ? y2 + 1 : y2;
74
+ return { ...base, year, month: m, day: d, hour, minute, second, millisecond };
75
+ }
76
+ function round(value, options) {
77
+ const base = asDateFieldView(value);
78
+ const mode = options.mode ?? "nearest";
79
+ const increment = (options.roundingIncrement ?? 1) * MS_PER_UNIT[options.unit];
80
+ if (increment <= 0) {
81
+ throw new Error(`temporal-fmt: round() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);
82
+ }
83
+ const ms = toMs(base);
84
+ const rounded = applyMode(ms, mode, increment);
85
+ return fromMs(rounded, base);
86
+ }
87
+ function floor(value, unit, roundingIncrement = 1) {
88
+ return round(value, { unit, mode: "floor", roundingIncrement });
89
+ }
90
+ function ceil(value, unit, roundingIncrement = 1) {
91
+ return round(value, { unit, mode: "ceil", roundingIncrement });
92
+ }
93
+ function truncate(value, unit, roundingIncrement = 1) {
94
+ return round(value, { unit, mode: "trunc", roundingIncrement });
95
+ }
96
+ function fieldToNs(v, nsPer, field) {
97
+ if (Number.isInteger(v)) return BigInt(v) * nsPer;
98
+ const scaled = v * Number(nsPer);
99
+ if (Number.isSafeInteger(scaled)) return BigInt(scaled);
100
+ throw new InvalidDurationError({
101
+ reason: `field "${field}" (${v}) cannot be converted to nanoseconds exactly \u2014 the value is too large for a fractional unit; use smaller units`
102
+ });
103
+ }
104
+ var DURATION_UNIT_TO_NS = {
105
+ years: 0n,
106
+ // calendar-bound, can't convert without relativeTo
107
+ months: 0n,
108
+ // same
109
+ weeks: 0n,
110
+ // same
111
+ days: 86400n * 1000000000n,
112
+ hours: 3600n * 1000000000n,
113
+ minutes: 60n * 1000000000n,
114
+ seconds: 1000000000n,
115
+ milliseconds: 1000000n,
116
+ microseconds: 1000n,
117
+ nanoseconds: 1n
118
+ };
119
+ function isCalendarBound(unit) {
120
+ return unit === "years" || unit === "months" || unit === "weeks";
121
+ }
122
+ function roundDuration(duration, options) {
123
+ const mode = options.mode ?? "nearest";
124
+ const increment = BigInt(options.roundingIncrement ?? 1);
125
+ if (increment <= 0n) {
126
+ throw new Error(`temporal-fmt: roundDuration() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);
127
+ }
128
+ if (isCalendarBound(options.unit)) {
129
+ throw new Error(
130
+ `temporal-fmt: roundDuration() to "${options.unit}" requires a Temporal.Duration with a relativeTo \u2014 this helper operates on plain field bags without calendar context. Use Temporal.Duration.prototype.round() directly.`
131
+ );
132
+ }
133
+ let totalNs = 0n;
134
+ for (const u of ["days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]) {
135
+ const v = duration[u] ?? 0;
136
+ totalNs += fieldToNs(v, DURATION_UNIT_TO_NS[u], u);
137
+ }
138
+ const stepNs = increment * DURATION_UNIT_TO_NS[options.unit];
139
+ const sign = totalNs < 0n ? -1n : 1n;
140
+ const abs = totalNs < 0n ? -totalNs : totalNs;
141
+ const stepped = abs / stepNs;
142
+ const remainder = abs % stepNs;
143
+ let rounded;
144
+ switch (mode) {
145
+ case "nearest":
146
+ rounded = remainder * 2n >= stepNs ? stepped + 1n : stepped;
147
+ break;
148
+ case "floor":
149
+ rounded = stepped;
150
+ break;
151
+ case "ceil":
152
+ rounded = remainder > 0n ? stepped + 1n : stepped;
153
+ break;
154
+ case "trunc":
155
+ rounded = stepped;
156
+ break;
157
+ }
158
+ const newTotalNs = sign * rounded * stepNs;
159
+ const result = { ...duration };
160
+ let remaining = newTotalNs;
161
+ const unitsInOrder = ["days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
162
+ const startIdx = unitsInOrder.indexOf(options.unit);
163
+ for (let i = startIdx + 1; i < unitsInOrder.length; i++) {
164
+ result[unitsInOrder[i]] = 0;
165
+ }
166
+ for (let i = 0; i <= startIdx; i++) {
167
+ const u = unitsInOrder[i];
168
+ const unitNs = DURATION_UNIT_TO_NS[u];
169
+ if (unitNs === 0n) continue;
170
+ const count = remaining / unitNs;
171
+ remaining -= count * unitNs;
172
+ result[u] = Number(count);
173
+ }
174
+ return result;
175
+ }
176
+
177
+ // src/formatDuration.ts
178
+ var DURATION_UNITS = {
179
+ y: { longSingular: "year", longPlural: "years", shortSingular: "yr", shortPlural: "yrs", field: "years", intlUnit: "year" },
180
+ o: { longSingular: "month", longPlural: "months", shortSingular: "mo", shortPlural: "mos", field: "months", intlUnit: "month" },
181
+ w: { longSingular: "week", longPlural: "weeks", shortSingular: "wk", shortPlural: "wks", field: "weeks", intlUnit: "week" },
182
+ d: { longSingular: "day", longPlural: "days", shortSingular: "d", shortPlural: "d", field: "days", intlUnit: "day" },
183
+ h: { longSingular: "hour", longPlural: "hours", shortSingular: "h", shortPlural: "h", field: "hours", intlUnit: "hour" },
184
+ m: { longSingular: "minute", longPlural: "minutes", shortSingular: "m", shortPlural: "m", field: "minutes", intlUnit: "minute" },
185
+ s: { longSingular: "second", longPlural: "seconds", shortSingular: "s", shortPlural: "s", field: "seconds", intlUnit: "second" },
186
+ S: { longSingular: "millisecond", longPlural: "milliseconds", shortSingular: "ms", shortPlural: "ms", field: "milliseconds", intlUnit: "millisecond" }
187
+ };
188
+ var DURATION_TOKEN_STRINGS = Object.keys(DURATION_UNITS).flatMap((single) => [single + single + single, single + single, single]).sort((a, b) => b.length - a.length);
189
+ function tokenizeDuration(format) {
190
+ const pieces = [];
191
+ let i = 0;
192
+ while (i < format.length) {
193
+ const ch = format[i];
194
+ if (ch === "'") {
195
+ if (format[i + 1] === "'") {
196
+ appendLiteral(pieces, "'");
197
+ i += 2;
198
+ continue;
199
+ }
200
+ let j = i + 1;
201
+ let literal = "";
202
+ let closed = false;
203
+ while (j < format.length) {
204
+ if (format[j] === "'") {
205
+ if (format[j + 1] === "'") {
206
+ literal += "'";
207
+ j += 2;
208
+ continue;
209
+ }
210
+ closed = true;
211
+ j += 1;
212
+ break;
213
+ }
214
+ literal += format[j];
215
+ j += 1;
216
+ }
217
+ if (!closed) {
218
+ throw new Error(`temporal-fmt: unterminated quote in duration format string "${format}"`);
219
+ }
220
+ appendLiteral(pieces, literal);
221
+ i = j;
222
+ continue;
223
+ }
224
+ const match = DURATION_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));
225
+ if (match) {
226
+ const unit = match[0];
227
+ const form = match.length === 1 ? "numeric" : match.length === 2 ? "short" : "long";
228
+ pieces.push({ kind: "token", value: match, unit, form });
229
+ i += match.length;
230
+ continue;
231
+ }
232
+ appendLiteral(pieces, ch);
233
+ i += 1;
234
+ }
235
+ return pieces;
236
+ }
237
+ function appendLiteral(pieces, value) {
238
+ const last = pieces[pieces.length - 1];
239
+ if (last && last.kind === "literal") {
240
+ last.value += value;
241
+ } else {
242
+ pieces.push({ kind: "literal", value });
243
+ }
244
+ }
245
+ function readUnit(duration, field) {
246
+ const value = duration[field];
247
+ if (value === void 0 || value === null) return 0;
248
+ if (typeof value === "number" && Number.isFinite(value)) return value;
249
+ const coerced = Number(value);
250
+ if (!Number.isFinite(coerced)) {
251
+ throw new Error(
252
+ `temporal-fmt: duration field "${field}" is not a finite number (got ${String(value)}).`
253
+ );
254
+ }
255
+ return coerced;
256
+ }
257
+ var unitFormatterCache = /* @__PURE__ */ new Map();
258
+ var MAX_UNIT_FORMATTER_CACHE_SIZE = 200;
259
+ function getUnitFormatter(locale, intlUnit, unitDisplay) {
260
+ const key = `${canonicalCacheKey(locale)}|${intlUnit}|${unitDisplay}`;
261
+ const cached = unitFormatterCache.get(key);
262
+ if (cached) return cached;
263
+ if (unitFormatterCache.size >= MAX_UNIT_FORMATTER_CACHE_SIZE) {
264
+ const oldestKey = unitFormatterCache.keys().next().value;
265
+ if (oldestKey !== void 0) unitFormatterCache.delete(oldestKey);
266
+ }
267
+ let formatter;
268
+ try {
269
+ formatter = new Intl.NumberFormat(normalizeLocaleTag(locale), {
270
+ style: "unit",
271
+ unit: intlUnit,
272
+ unitDisplay
273
+ });
274
+ } catch (err) {
275
+ throw new InvalidLocaleError({ actual: locale, reason: err.message });
276
+ }
277
+ unitFormatterCache.set(key, formatter);
278
+ return formatter;
279
+ }
280
+ function formatDuration(duration, formatStr, options = {}) {
281
+ return renderDurationPieces(duration, formatStr, options).map((p) => p.value).join("");
282
+ }
283
+ function renderDurationPieces(duration, formatStr, options = {}) {
284
+ if (formatStr.length > MAX_FORMAT_LENGTH) {
285
+ throw new Error(
286
+ `temporal-fmt: duration format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
287
+ );
288
+ }
289
+ const showZeroes = options.showZeroValues === true;
290
+ const locale = options.locale;
291
+ const useIntl = locale !== void 0;
292
+ const pieces = tokenizeDuration(formatStr);
293
+ const out = [];
294
+ for (const piece of pieces) {
295
+ if (piece.kind === "literal") {
296
+ out.push({ kind: "literal", value: piece.value });
297
+ continue;
298
+ }
299
+ const unit = DURATION_UNITS[piece.unit];
300
+ if (!unit) {
301
+ throw new Error(`temporal-fmt: unknown duration token "${piece.value}"`);
302
+ }
303
+ const value = readUnit(duration, unit.field);
304
+ if (value === 0 && !showZeroes) {
305
+ out.push({ kind: "token", value: "", token: piece.value });
306
+ continue;
307
+ }
308
+ if (piece.form === "numeric") {
309
+ out.push({ kind: "token", value: String(value), token: piece.value });
310
+ continue;
311
+ }
312
+ if (useIntl) {
313
+ const unitDisplay = piece.form === "short" ? "short" : "long";
314
+ const formatter = getUnitFormatter(locale, unit.intlUnit, unitDisplay);
315
+ out.push({ kind: "token", value: formatter.format(value), token: piece.value });
316
+ continue;
317
+ }
318
+ if (piece.form === "short") {
319
+ out.push({ kind: "token", value: value + (value === 1 || value === -1 ? unit.shortSingular : unit.shortPlural), token: piece.value });
320
+ } else {
321
+ out.push({ kind: "token", value: value + " " + (value === 1 || value === -1 ? unit.longSingular : unit.longPlural), token: piece.value });
322
+ }
323
+ }
324
+ return out;
325
+ }
326
+
327
+ // src/duration.ts
328
+ function formatDurationToParts(duration, formatStr, options = {}) {
329
+ return renderDurationPieces(duration, formatStr, options).map(
330
+ (p) => p.kind === "token" ? { type: "token", value: p.value, token: p.token } : { type: "literal", value: p.value }
331
+ );
332
+ }
333
+ function parseISODuration(input) {
334
+ const re = /^P(?:(?:(\d+(?:\.\d+)?)Y)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)W)?(?:(\d+(?:\.\d+)?)D)?)?(?:T(?:(\d+(?:\.\d+)?)H)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)S)?)?$/;
335
+ const m = re.exec(input);
336
+ if (!m) {
337
+ throw new InvalidDurationError({ input, reason: "does not match ISO 8601 duration grammar (P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S)" });
338
+ }
339
+ if (m.slice(1).every((g) => g === void 0)) {
340
+ throw new InvalidDurationError({ input, reason: 'duration has no fields (use "P0D" for zero duration)' });
341
+ }
342
+ const toNum = (s) => s === void 0 ? 0 : Number(s);
343
+ return {
344
+ years: toNum(m[1]),
345
+ months: toNum(m[2]),
346
+ weeks: toNum(m[3]),
347
+ days: toNum(m[4]),
348
+ hours: toNum(m[5]),
349
+ minutes: toNum(m[6]),
350
+ seconds: toNum(m[7])
351
+ };
352
+ }
353
+ function formatISODuration(duration) {
354
+ const parts = ["P"];
355
+ const years = duration.years ?? 0;
356
+ const months = duration.months ?? 0;
357
+ const weeks = duration.weeks ?? 0;
358
+ const days = duration.days ?? 0;
359
+ const hours = duration.hours ?? 0;
360
+ const minutes = duration.minutes ?? 0;
361
+ const seconds = duration.seconds ?? 0;
362
+ if (years) parts.push(`${years}Y`);
363
+ if (months) parts.push(`${months}M`);
364
+ if (weeks) parts.push(`${weeks}W`);
365
+ if (days) parts.push(`${days}D`);
366
+ if (hours || minutes || seconds) {
367
+ parts.push("T");
368
+ if (hours) parts.push(`${hours}H`);
369
+ if (minutes) parts.push(`${minutes}M`);
370
+ if (seconds) parts.push(`${seconds}S`);
371
+ }
372
+ if (parts.length === 1) parts.push("0D");
373
+ return parts.join("");
374
+ }
375
+ function tokenizeDurationFormat(formatStr) {
376
+ const DURATION_TOKEN_STRINGS2 = ["y", "o", "w", "d", "h", "m", "s", "S"].flatMap((single) => [single + single + single, single + single, single]).sort((a, b) => b.length - a.length);
377
+ const pieces = [];
378
+ let i = 0;
379
+ while (i < formatStr.length) {
380
+ const ch = formatStr[i];
381
+ if (ch === "'") {
382
+ if (formatStr[i + 1] === "'") {
383
+ appendLiteral2(pieces, "'");
384
+ i += 2;
385
+ continue;
386
+ }
387
+ let j = i + 1;
388
+ let literal = "";
389
+ let closed = false;
390
+ while (j < formatStr.length) {
391
+ if (formatStr[j] === "'") {
392
+ if (formatStr[j + 1] === "'") {
393
+ literal += "'";
394
+ j += 2;
395
+ continue;
396
+ }
397
+ closed = true;
398
+ j += 1;
399
+ break;
400
+ }
401
+ literal += formatStr[j];
402
+ j += 1;
403
+ }
404
+ if (!closed) throw new FormatSyntaxError({ format: formatStr, reason: "unterminated quote" });
405
+ appendLiteral2(pieces, literal);
406
+ i = j;
407
+ continue;
408
+ }
409
+ const match = DURATION_TOKEN_STRINGS2.find((tok) => formatStr.startsWith(tok, i));
410
+ if (match) {
411
+ pieces.push({ kind: "token", value: match, unit: match[0] });
412
+ i += match.length;
413
+ continue;
414
+ }
415
+ appendLiteral2(pieces, ch);
416
+ i += 1;
417
+ }
418
+ return pieces;
419
+ }
420
+ function appendLiteral2(pieces, value) {
421
+ const last = pieces[pieces.length - 1];
422
+ if (last && last.kind === "literal") {
423
+ last.value += value;
424
+ } else {
425
+ pieces.push({ kind: "literal", value });
426
+ }
427
+ }
428
+ function parseDuration(input, formatStr, options = {}) {
429
+ const tokens = tokenizeDurationFormat(formatStr);
430
+ const fields = {};
431
+ const groupSpecs = [];
432
+ let regex = "";
433
+ for (const tok of tokens) {
434
+ if (tok.kind === "literal") {
435
+ regex += tok.value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
436
+ } else {
437
+ const unit = tok.unit;
438
+ const form = tok.value.length === 1 ? "numeric" : tok.value.length === 2 ? "short" : "long";
439
+ /* c8 ignore start @preserve -- the final `: null` branch, and the
440
+ `if (key === null)` throw it feeds, are both unreachable: tok.unit
441
+ is always one of the 8 letters in DURATION_TOKEN_STRINGS (the same
442
+ source set this ternary checks against), so `null` can't be
443
+ produced by any token the tokenizer emits. Kept as a guard against
444
+ a future letter being added to one set without the other. */
445
+ const key = unit === "y" ? "years" : unit === "o" ? "months" : unit === "w" ? "weeks" : unit === "d" ? "days" : unit === "h" ? "hours" : unit === "m" ? "minutes" : unit === "s" ? "seconds" : unit === "S" ? "milliseconds" : null;
446
+ if (key === null) {
447
+ throw new FormatSyntaxError({ format: formatStr, token: tok.value, reason: "unknown duration token" });
448
+ }
449
+ /* c8 ignore stop @preserve */
450
+ if (form === "numeric") {
451
+ regex += "(-?\\d+)";
452
+ } else {
453
+ regex += "(\\d+)\\s*\\w*";
454
+ }
455
+ groupSpecs.push({ unit: key, form });
456
+ }
457
+ }
458
+ const re = new RegExp(`^${regex}$`, "u");
459
+ const m = re.exec(input);
460
+ if (!m) {
461
+ throw new FormatSyntaxError({ input, format: formatStr, reason: "does not match duration format" });
462
+ }
463
+ groupSpecs.forEach((spec, i) => {
464
+ const raw = m[i + 1];
465
+ const value = Number(raw);
466
+ if (!Number.isFinite(value)) {
467
+ throw new InvalidDurationError({ input, reason: `token for ${spec.unit} matched "${raw}" but isn't a finite number` });
468
+ }
469
+ fields[spec.unit] = value;
470
+ });
471
+ return fields;
472
+ }
473
+ function balanceDuration(duration) {
474
+ const NS_PER = [
475
+ ["days", 86400n * 1000000000n],
476
+ ["hours", 3600n * 1000000000n],
477
+ ["minutes", 60n * 1000000000n],
478
+ ["seconds", 1000000000n],
479
+ ["milliseconds", 1000000n],
480
+ ["microseconds", 1000n],
481
+ ["nanoseconds", 1n]
482
+ ];
483
+ let totalNs = 0n;
484
+ for (const [k, nsPer] of NS_PER) {
485
+ const v = duration[k];
486
+ if (typeof v === "number") totalNs += fieldToNs(v, nsPer, k);
487
+ }
488
+ const result = { ...duration };
489
+ let remaining = totalNs;
490
+ for (const [k, nsPer] of NS_PER) {
491
+ const count = remaining / nsPer;
492
+ remaining -= count * nsPer;
493
+ result[k] = Number(count);
494
+ }
495
+ return result;
496
+ }
497
+ function totalDuration(duration, unit) {
498
+ const NS_PER = {
499
+ days: 86400n * 1000000000n,
500
+ hours: 3600n * 1000000000n,
501
+ minutes: 60n * 1000000000n,
502
+ seconds: 1000000000n,
503
+ milliseconds: 1000000n,
504
+ microseconds: 1000n,
505
+ nanoseconds: 1n
506
+ };
507
+ let totalNs = 0n;
508
+ for (const k of Object.keys(NS_PER)) {
509
+ const v = duration[k];
510
+ if (typeof v === "number") totalNs += fieldToNs(v, NS_PER[k], k);
511
+ }
512
+ const divisor = NS_PER[unit];
513
+ if (!divisor) {
514
+ throw new InvalidDurationError({ reason: `totalDuration() does not support unit "${unit}" (calendar-bound units need a relativeTo)` });
515
+ }
516
+ return Number(totalNs) / Number(divisor);
517
+ }
518
+ function compareDuration(a, b) {
519
+ const aNs = totalDurationNs(a);
520
+ const bNs = totalDurationNs(b);
521
+ if (aNs < bNs) return -1;
522
+ if (aNs > bNs) return 1;
523
+ return 0;
524
+ }
525
+ function totalDurationNs(d) {
526
+ const NS_PER = {
527
+ days: 86400n * 1000000000n,
528
+ hours: 3600n * 1000000000n,
529
+ minutes: 60n * 1000000000n,
530
+ seconds: 1000000000n,
531
+ milliseconds: 1000000n,
532
+ microseconds: 1000n,
533
+ nanoseconds: 1n
534
+ };
535
+ let total = 0n;
536
+ for (const k of Object.keys(NS_PER)) {
537
+ const v = d[k];
538
+ if (typeof v === "number") total += fieldToNs(v, NS_PER[k], k);
539
+ }
540
+ return total;
541
+ }
542
+ function addDuration(a, b) {
543
+ const keys = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
544
+ const result = {};
545
+ for (const k of keys) {
546
+ result[k] = (a[k] ?? 0) + (b[k] ?? 0);
547
+ }
548
+ return result;
549
+ }
550
+ function subtractDuration(a, b) {
551
+ return addDuration(a, negateDuration(b));
552
+ }
553
+ function negateDuration(d) {
554
+ const result = {};
555
+ for (const k of Object.keys(d)) {
556
+ result[k] = -(d[k] ?? 0);
557
+ }
558
+ return result;
559
+ }
560
+
561
+ export {
562
+ round,
563
+ floor,
564
+ ceil,
565
+ truncate,
566
+ roundDuration,
567
+ formatDuration,
568
+ formatDurationToParts,
569
+ parseISODuration,
570
+ formatISODuration,
571
+ parseDuration,
572
+ balanceDuration,
573
+ totalDuration,
574
+ compareDuration,
575
+ addDuration,
576
+ subtractDuration
577
+ };
578
+ //# sourceMappingURL=chunk-ACKKWH5Z.js.map