temporal-fmt 0.9.0 → 0.9.2

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 (105) hide show
  1. package/dist/calendarUtils.cjs +4 -259
  2. package/dist/calendarUtils.cjs.map +1 -1
  3. package/dist/calendarUtils.js +1 -33
  4. package/dist/chunk-2JHEV7ZF.js +7 -0
  5. package/dist/chunk-2JHEV7ZF.js.map +1 -0
  6. package/dist/chunk-3MZLTVP3.js +2 -0
  7. package/dist/{chunk-MXXYIMFJ.js.map → chunk-3MZLTVP3.js.map} +1 -1
  8. package/dist/chunk-4JWGUR4O.js +11 -0
  9. package/dist/{chunk-AXQLUIDY.js.map → chunk-4JWGUR4O.js.map} +1 -1
  10. package/dist/chunk-5U5WJ465.js +36 -0
  11. package/dist/chunk-5U5WJ465.js.map +1 -0
  12. package/dist/chunk-C5YESWFT.js +2 -0
  13. package/dist/chunk-C5YESWFT.js.map +1 -0
  14. package/dist/chunk-C6ZZ62ET.js +2 -0
  15. package/dist/{chunk-YS52LOMJ.js.map → chunk-C6ZZ62ET.js.map} +1 -1
  16. package/dist/chunk-EHLRZULM.js +5 -0
  17. package/dist/{chunk-4ZBGVYLR.js.map → chunk-EHLRZULM.js.map} +1 -1
  18. package/dist/chunk-F4RGUDA3.js +2 -0
  19. package/dist/chunk-F4RGUDA3.js.map +1 -0
  20. package/dist/chunk-J2AQNXF2.js +2 -0
  21. package/dist/{chunk-GRJQHQLW.js.map → chunk-J2AQNXF2.js.map} +1 -1
  22. package/dist/chunk-NBXF7V5B.js +2 -0
  23. package/dist/chunk-NBXF7V5B.js.map +1 -0
  24. package/dist/chunk-R52YOKI3.js +7 -0
  25. package/dist/{chunk-M33NSC4E.js.map → chunk-R52YOKI3.js.map} +1 -1
  26. package/dist/chunk-UJR6DHXY.js +2 -0
  27. package/dist/chunk-UJR6DHXY.js.map +1 -0
  28. package/dist/chunk-VQXUMFB2.js +21 -0
  29. package/dist/{chunk-VHSZL3P3.js.map → chunk-VQXUMFB2.js.map} +1 -1
  30. package/dist/chunk-XH46JNPK.js +2 -0
  31. package/dist/chunk-XH46JNPK.js.map +1 -0
  32. package/dist/chunk-YB6YAG7E.js +35 -0
  33. package/dist/chunk-YB6YAG7E.js.map +1 -0
  34. package/dist/chunk-ZGMTY52O.js +2 -0
  35. package/dist/chunk-ZGMTY52O.js.map +1 -0
  36. package/dist/duration.cjs +9 -624
  37. package/dist/duration.cjs.map +1 -1
  38. package/dist/duration.js +1 -29
  39. package/dist/format.cjs +18 -769
  40. package/dist/format.cjs.map +1 -1
  41. package/dist/format.js +1 -20
  42. package/dist/formatDuration.d.cts +6 -0
  43. package/dist/formatDuration.d.ts +6 -0
  44. package/dist/index.cjs +146 -6672
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.js +58 -3004
  47. package/dist/index.js.map +1 -1
  48. package/dist/interval.cjs +18 -952
  49. package/dist/interval.cjs.map +1 -1
  50. package/dist/interval.js +1 -41
  51. package/dist/localeRegistry.cjs +15 -328
  52. package/dist/localeRegistry.cjs.map +1 -1
  53. package/dist/localeRegistry.js +1 -11
  54. package/dist/localeVocab.d.cts +3 -0
  55. package/dist/localeVocab.d.ts +3 -0
  56. package/dist/parse.cjs +53 -1637
  57. package/dist/parse.cjs.map +1 -1
  58. package/dist/parse.js +1 -20
  59. package/dist/parsePattern.d.cts +1 -0
  60. package/dist/parsePattern.d.ts +1 -0
  61. package/dist/pattern.d.cts +1 -0
  62. package/dist/pattern.d.ts +1 -0
  63. package/dist/recurrence.cjs +20 -450
  64. package/dist/recurrence.cjs.map +1 -1
  65. package/dist/recurrence.js +1 -20
  66. package/dist/relativeTime.cjs +24 -177
  67. package/dist/relativeTime.cjs.map +1 -1
  68. package/dist/relativeTime.js +1 -14
  69. package/dist/rounding.d.cts +1 -0
  70. package/dist/rounding.d.ts +1 -0
  71. package/dist/serialization.d.cts +1 -1
  72. package/dist/serialization.d.ts +1 -1
  73. package/dist/timezone.cjs +20 -387
  74. package/dist/timezone.cjs.map +1 -1
  75. package/dist/timezone.js +1 -26
  76. package/dist/typeGuards.d.cts +9 -9
  77. package/dist/typeGuards.d.ts +9 -9
  78. package/package.json +4 -2
  79. package/dist/chunk-4ZBGVYLR.js +0 -182
  80. package/dist/chunk-AXQLUIDY.js +0 -183
  81. package/dist/chunk-CKVHXFX7.js +0 -902
  82. package/dist/chunk-CKVHXFX7.js.map +0 -1
  83. package/dist/chunk-CSUIKL73.js +0 -215
  84. package/dist/chunk-CSUIKL73.js.map +0 -1
  85. package/dist/chunk-EDUFSRWJ.js +0 -9
  86. package/dist/chunk-EDUFSRWJ.js.map +0 -1
  87. package/dist/chunk-FQA4XJME.js +0 -57
  88. package/dist/chunk-FQA4XJME.js.map +0 -1
  89. package/dist/chunk-FTMLZ4EN.js +0 -196
  90. package/dist/chunk-FTMLZ4EN.js.map +0 -1
  91. package/dist/chunk-GRJQHQLW.js +0 -170
  92. package/dist/chunk-JCIMP43G.js +0 -602
  93. package/dist/chunk-JCIMP43G.js.map +0 -1
  94. package/dist/chunk-KDYK72Z2.js +0 -75
  95. package/dist/chunk-KDYK72Z2.js.map +0 -1
  96. package/dist/chunk-M33NSC4E.js +0 -169
  97. package/dist/chunk-MXXYIMFJ.js +0 -29
  98. package/dist/chunk-NFKQ3EXC.js +0 -188
  99. package/dist/chunk-NFKQ3EXC.js.map +0 -1
  100. package/dist/chunk-O6GVMBLW.js +0 -403
  101. package/dist/chunk-O6GVMBLW.js.map +0 -1
  102. package/dist/chunk-VHSZL3P3.js +0 -242
  103. package/dist/chunk-VSUCBUTC.js +0 -295
  104. package/dist/chunk-VSUCBUTC.js.map +0 -1
  105. package/dist/chunk-YS52LOMJ.js +0 -51
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interval.ts"],"sourcesContent":["// Intervals / ranges. An interval is a pair of\n// (start, end) Temporal values with bounds semantics: open, closed,\n// or half-open. Operations: contains, overlaps, intersects, isBefore,\n// isAfter, intersection, union, difference, subtract, mergeIntervals,\n// splitInterval. Plus formatRange / formatRangeToParts with intelligent\n// collapsing.\n//\n// All operations are field-based (no instanceof), matching the rest of\n// the library. Internally uses the comparison helpers from comparison.ts\n// to order endpoints.\n\nimport { compare } from './comparison.js';\nimport { format, formatToParts, type FormattedPart } from './format.js';\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport type { FormatOptions } from './tokens.js';\n\nexport type IntervalBounds = 'open' | 'closed' | 'half-open-start' | 'half-open-end';\n\nexport interface Interval {\n start: unknown;\n end: unknown;\n bounds: IntervalBounds;\n}\n\nexport function interval(start: unknown, end: unknown, bounds: IntervalBounds = 'closed'): Interval {\n // Validate that start ≤ end, otherwise the interval is malformed.\n const cmp = compare(start, end);\n if (cmp > 0) {\n throw new Error(\n `temporal-fmt: interval start must be ≤ end (got start > end). Pass them in chronological order.`\n );\n }\n return { start, end, bounds };\n}\n\n// Inclusive containment check accounting for bounds semantics.\n// - 'closed': [start, end] — both endpoints included\n// - 'open': (start, end) — neither endpoint included\n// - 'half-open-start': (start, end] — start excluded, end included\n// - 'half-open-end': [start, end) — start included, end excluded\nexport function contains(iv: Interval, value: unknown): boolean {\n const startCmp = compare(value, iv.start);\n const endCmp = compare(value, iv.end);\n const afterStart = iv.bounds === 'open' || iv.bounds === 'half-open-end'\n ? startCmp > 0\n : startCmp >= 0;\n const beforeEnd = iv.bounds === 'open' || iv.bounds === 'half-open-start'\n ? endCmp < 0\n : endCmp <= 0;\n return afterStart && beforeEnd;\n}\n\n// Two intervals overlap iff their ranges intersect, regardless of bounds.\n// intersects() is the boolean form; intersection() returns the actual\n// overlap interval.\nexport function overlaps(a: Interval, b: Interval): boolean {\n return intersects(a, b);\n}\n\nexport function intersects(a: Interval, b: Interval): boolean {\n // a is entirely before b: a.end < b.start\n if (compare(a.end, b.start) < 0) return false;\n // a is entirely after b: a.start > b.end\n if (compare(a.start, b.end) > 0) return false;\n // Touching endpoints with both-closed bounds counts as overlap;\n // open bounds would not. To keep it simple, touching counts.\n return true;\n}\n\nexport function isBefore(a: Interval, b: Interval): boolean {\n return compare(a.end, b.start) < 0;\n}\n\nexport function isAfter(a: Interval, b: Interval): boolean {\n return compare(a.start, b.end) > 0;\n}\n\n// Returns the intersection of two intervals, or null if they don't\n// overlap. The result's bounds are the more restrictive of the two\n// inputs (closed ∩ open = open, etc.).\nexport function intersection(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) >= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) <= 0 ? a.end : b.end;\n // Compute resulting bounds: take the more restrictive at each endpoint.\n // If a.start === b.start, the more restrictive of (a.bounds, b.bounds)\n // at that endpoint wins.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n || (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n || (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the union of two intervals (the smallest interval containing\n// both), or null if they don't overlap (caller should use mergeIntervals\n// for that case).\nexport function union(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) <= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) >= 0 ? a.end : b.end;\n // Union's bounds are the less restrictive at each endpoint.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n && (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n && (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the part of `a` that is not in `b`. May produce 0, 1, or 2\n// intervals depending on the overlap shape.\n//\n// Bounds semantics: each surviving piece keeps the inclusivity of the\n// original endpoint it inherits from `a`, and the endpoint created by the\n// cut is always exclusive (it is `b`'s edge, which belongs to the removed\n// overlap). The old implementation derived both bounds from a lossy\n// helper that collapsed several of these combinations — the pieces'\n// endpoints were right, but the bounds metadata was stricter than the\n// actual remainder for open/half-open inputs.\nexport function difference(a: Interval, b: Interval): Interval[] {\n if (!intersects(a, b)) return [a];\n const result: Interval[] = [];\n // Does `a` include its own start / end?\n const aStartIncluded = a.bounds === 'closed' || a.bounds === 'half-open-end';\n const aEndIncluded = a.bounds === 'closed' || a.bounds === 'half-open-start';\n // Part of `a` before `b` starts: [a.start, b.start) — start inclusivity\n // inherited from a, cut end always exclusive.\n if (compare(a.start, b.start) < 0) {\n result.push({ start: a.start, end: b.start, bounds: aStartIncluded ? 'half-open-end' : 'open' });\n }\n // Part of `a` after `b` ends: (b.end, a.end] — cut start always\n // exclusive, end inclusivity inherited from a.\n if (compare(a.end, b.end) > 0) {\n result.push({ start: b.end, end: a.end, bounds: aEndIncluded ? 'half-open-start' : 'open' });\n }\n return result;\n}\n\n// Alias for difference() — \"subtract\" reads more naturally at some\n// call sites than \"difference\" does.\nexport const subtract = difference;\n\n// Merges a list of intervals, combining overlapping ones. Returns a\n// sorted list of disjoint intervals. The inputs are treated as\n// immutable — the merged result carries shallow copies of any interval\n// object it extends, so a caller's own `end` reference is never\n// reassigned under them (mergeIntervals used to write `last.end =\n// current.end` straight into the caller's object).\nexport function mergeIntervals(intervals: Interval[]): Interval[] {\n if (intervals.length === 0) return [];\n const sorted = [...intervals].sort((a, b) => compare(a.start, b.start));\n const result: Interval[] = [{ ...sorted[0]! }];\n for (let i = 1; i < sorted.length; i++) {\n const current = sorted[i]!;\n const last = result[result.length - 1]!;\n if (intersects(last, current) || compare(last.end, current.start) === 0) {\n // Merge into last.\n if (compare(current.end, last.end) > 0) {\n last.end = current.end;\n }\n } else {\n result.push(current);\n }\n }\n return result;\n}\n\n// Widest instant JS Date can represent (±8.64e15 ms, i.e. ±100,000,000\n// days from the Unix epoch). splitInterval rebuilds endpoints via Date,\n// so anything outside this window would silently produce Invalid Date\n// objects with NaN year/month/day fields — reject it descriptively\n// instead.\nconst MAX_DATE_MS = 8.64e15;\n\n// Splits an interval into N equal sub-intervals. Throws if N ≤ 0.\n// Equality is by ms-distance between start and end — for date ranges\n// this approximates \"equal time slices\", which is the most useful\n// interpretation. For calendar-bound splits (e.g. \"split this month\n// into weeks\"), callers should use recurrence() instead.\nexport function splitInterval(iv: Interval, n: number): Interval[] {\n if (n <= 0) throw new Error(`temporal-fmt: splitInterval requires n > 0 (got ${n}).`);\n if (n === 1) return [iv];\n const startFields = asDateFieldView(iv.start) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const endFields = asDateFieldView(iv.end) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const startMs = toMs(startFields);\n const endMs = toMs(endFields);\n if (Math.abs(startMs) > MAX_DATE_MS || Math.abs(endMs) > MAX_DATE_MS) {\n throw new RangeError(\n `temporal-fmt: splitInterval() endpoints are outside the representable Date range ` +\n `(approximately ±275,760 years). Split the interval into smaller ranges first.`\n );\n }\n const step = (endMs - startMs) / n;\n const result: Interval[] = [];\n for (let i = 0; i < n; i++) {\n const sliceStart = startMs + i * step;\n const sliceEnd = i === n - 1 ? endMs : sliceStart + step;\n result.push({\n start: fromMs(sliceStart, startFields),\n end: fromMs(sliceEnd, endFields),\n bounds: i === 0 ? iv.bounds : 'half-open-start',\n });\n }\n return result;\n}\n\nfunction toMs(v: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): number {\n // Reuse the same day-count math arithmetic.ts uses, inlined.\n const y = v.year!, m = v.month!, d = v.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 return days * 86_400_000\n + (v.hour ?? 0) * 3_600_000\n + (v.minute ?? 0) * 60_000\n + (v.second ?? 0) * 1_000\n + (v.millisecond ?? 0);\n}\n\nfunction fromMs(ms: number, base: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): DateFieldView {\n const MS_PER_DAY = 86_400_000;\n const dayOverflow = Math.floor(ms / MS_PER_DAY);\n let withinDay = ms % MS_PER_DAY;\n if (withinDay < 0) withinDay += MS_PER_DAY;\n const hour = Math.floor(withinDay / 3_600_000);\n const minute = Math.floor((withinDay % 3_600_000) / 60_000);\n const second = Math.floor((withinDay % 60_000) / 1_000);\n const millisecond = withinDay % 1_000;\n // `ms` (and therefore `dayOverflow`) is already an absolute epoch-ms /\n // epoch-day value — toMs() computed it via Howard Hinnant's algorithm\n // from scratch, not as an offset from `base`. So the date only needs\n // reconstructing from the Unix epoch itself; `base` is just a template\n // for whatever extra (non-date) fields get spread into the result.\n // (Previously this added dayOverflow on top of base's own epoch-ms,\n // double-counting base's offset and landing tens of thousands of days\n // away from the intended date for any non-epoch base.)\n const newMs = dayOverflow * MS_PER_DAY;\n const d = new Date(newMs);\n return {\n ...base,\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n hour,\n minute,\n second,\n millisecond,\n };\n}\n\n// Formats an interval as a single string, collapsing shared fields.\n// E.g. \"August 4 – August 6, 2026\" rather than \"August 4, 2026 – August 6, 2026\".\n// Uses Intl.DateTimeFormat's formatRange when available (most engines),\n// falling back to format(start, fmt) + '–' + format(end, fmt) when not.\n\nexport function formatRange(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): string {\n // The formatStr parameter is this function's contract — the caller\n // asked for their token format on both endpoints. Honor it first:\n // format each side with the caller's format string and join with an\n // en dash. (This used to try Intl.DateTimeFormat.formatRange() first,\n // which ignores formatStr entirely — formatRange(iv, 'yyyy-MM-dd')\n // silently returned locale-default output like \"8/4/2026 – 8/6/2026\"\n // instead of \"2026-08-04 – 2026-08-06\".) Intl's range collapsing is\n // kept as a fallback for inputs the token path can't render.\n try {\n const startStr = format(iv.start as Parameters<typeof format>[0], formatStr, options);\n const endStr = format(iv.end as Parameters<typeof format>[0], formatStr, options);\n return `${startStr} – ${endStr}`;\n } catch (err) {\n // Fallback: Intl.DateTimeFormat.formatRange is the standard\n // mechanism with native field-collapse behavior, but it has no\n // knowledge of the caller's token format string.\n try {\n const fmt = new Intl.DateTimeFormat(normalizeLocaleTag(options.locale ?? 'en-US'), {});\n // Cast the interval endpoints to Date for the Intl call. Both\n // endpoints must be Temporal values that can convert to a Date;\n // for non-ZonedDateTime values we synthesize a Date via the field\n // shape (year/month/day/hour/...).\n const startDate = toJSDate(iv.start);\n const endDate = toJSDate(iv.end);\n return fmt.formatRange(startDate, endDate);\n } catch {\n // Neither path could render — surface the token-path error, since\n // the formatStr contract is the primary one.\n throw err;\n }\n }\n}\n\nexport function formatRangeToParts(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): FormattedPart[] {\n // Best-effort: returns formatToParts for the start, then a literal\n // \" – \" part, then formatToParts for the end. Doesn't currently\n // expose Intl.DateTimeFormat.formatRangeToParts's collapsed shape —\n // that would require mapping Intl's part types back to temporal-fmt's\n // token strings, which is locale-dependent and not stable across\n // engines.\n const startParts = formatToParts(iv.start as Parameters<typeof formatToParts>[0], formatStr, options);\n const endParts = formatToParts(iv.end as Parameters<typeof formatToParts>[0], formatStr, options);\n const result: FormattedPart[] = [...startParts];\n result.push({ type: 'literal', value: ' – ' });\n result.push(...endParts);\n return result;\n}\n\nfunction toJSDate(value: unknown): Date {\n const v = value as { year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number; toInstant?: () => { epochMilliseconds: number } };\n if (typeof v?.toInstant === 'function') {\n return new Date(v.toInstant().epochMilliseconds);\n }\n if (typeof v?.year === 'number' && typeof v?.month === 'number' && typeof v?.day === 'number') {\n return new Date(Date.UTC(v.year, v.month - 1, v.day, v.hour ?? 0, v.minute ?? 0, v.second ?? 0, v.millisecond ?? 0));\n }\n throw new Error(`temporal-fmt: formatRange() expected Temporal values with year/month/day, got ${typeof value}.`);\n}"],"mappings":"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","names":["interval","start","end","bounds","compare","contains","iv","value","startCmp","endCmp","afterStart","beforeEnd","overlaps","a","b","intersects","isBefore","isAfter","intersection","startBoundsOpen","endBoundsOpen","union","difference","result","aStartIncluded","aEndIncluded","subtract","mergeIntervals","intervals","sorted","i","current","last","MAX_DATE_MS","splitInterval","startFields","asDateFieldView","endFields","startMs","toMs","endMs","step","sliceStart","sliceEnd","fromMs","v","y","m","d","y2","era","yoe","m2","doy","doe","ms","base","dayOverflow","withinDay","hour","minute","second","millisecond","newMs","formatRange","formatStr","options","startStr","format","endStr","err","fmt","normalizeLocaleTag","startDate","toJSDate","endDate","formatRangeToParts","startParts","formatToParts","endParts"]}
@@ -0,0 +1,2 @@
1
+ import{c as f}from"./chunk-3MZLTVP3.js";import{a as d}from"./chunk-VQXUMFB2.js";function l(e,n,o={}){let t=f(),r=o.disambiguation??"compatible",m={overflow:o.overflow??"constrain",disambiguation:r,offset:o.offset??"prefer"};try{return t.ZonedDateTime.from({year:e.year,month:e.month,day:e.day,hour:e.hour??0,minute:e.minute??0,second:e.second??0,millisecond:e.millisecond??0,microsecond:e.microsecond??0,nanosecond:e.nanosecond??0,timeZone:n},m)}catch(a){let u=a.message;throw/no such (wall-clock )?time/i.test(u)&&r==="reject"?new Error(`temporal-fmt: "${n}" has no such wall-clock time on ${e.year}-${e.month}-${e.day}T${e.hour??0}:${e.minute??0} \u2014 it falls in a DST gap. Pass { disambiguation: 'compatible' | 'earlier' | 'later' } to pick an instant.`):a}}function h(e){let n=e;if(typeof n?.timeZoneId!="string")throw new Error(`temporal-fmt: getTimeZone() expected a ZonedDateTime, got ${typeof e}.`);return n.timeZoneId}function w(e){let n=e;if(typeof n?.offset!="string")throw new Error(`temporal-fmt: getOffset() expected a ZonedDateTime, got ${typeof e}.`);return n.offset}function Z(e){let n=e;if(typeof n?.offsetNanoseconds=="number")return n.offsetNanoseconds;if(typeof n?.offset=="string"){let o=n.offset.match(/^([+-])(\d{2}):(\d{2})$/);if(!o)throw new Error(`temporal-fmt: getOffsetNanoseconds() couldn't parse offset "${n.offset}".`);return(o[1]==="-"?-1:1)*(Number(o[2])*3600+Number(o[3])*60)*1e9}throw new Error(`temporal-fmt: getOffsetNanoseconds() expected a ZonedDateTime, got ${typeof e}.`)}function T(e){let n=e;if(typeof n?.timeZoneId!="string"||typeof n?.offset!="string"||typeof n?.year!="number")throw new Error(`temporal-fmt: isDST() expected a ZonedDateTime, got ${typeof e}.`);let o=f();try{return o.ZonedDateTime.from({year:n.year,month:1,day:1,hour:12,timeZone:n.timeZoneId},{disambiguation:"compatible"}).offset!==n.offset}catch{return!1}}function k(e){return b(e,1)}function D(e){return b(e,-1)}function b(e,n){let o=e;if(typeof o?.timeZoneId!="string")throw new Error(`temporal-fmt: ${n>0?"getNextTransition":"getPreviousTransition"}() expected a ZonedDateTime, got ${typeof e}.`);let t=f(),r=o.timeZoneId,s=d(e,n,"days"),m=o.offset;for(let a=0;a<365*2;a++){let u=s,c=t.ZonedDateTime.from({...u,timeZone:r},{disambiguation:"compatible"});if(c.offset!==m)return c;s=d(s,n,"days")}}function x(e,n){let o=[],t=e;if(typeof t?.timeZoneId!="string")throw new Error(`temporal-fmt: getTransitions() expected a ZonedDateTime for start, got ${typeof e}.`);let r=n,s=f(),m=t.timeZoneId,a=d(e,1,"days"),u=t.offset;for(let c=0;c<365*5;c++){let p=a,i=s.ZonedDateTime.from({...p,timeZone:m},{disambiguation:"compatible"});if(i.offset!==u){if(i.year>r.year||i.year===r.year&&(i.month>r.month||i.month===r.month&&i.day>r.day))break;o.push(i),u=i.offset}a=d(a,1,"days")}return o}function I(e,n){let o=f(),t=o.ZonedDateTime.from({...e,timeZone:n},{disambiguation:"earlier",offset:"ignore"}),r=o.ZonedDateTime.from({...e,timeZone:n},{disambiguation:"later",offset:"ignore"}),s=m=>m.year===e.year&&m.month===e.month&&m.day===e.day&&m.hour===e.hour&&m.minute===e.minute&&m.second===e.second&&m.millisecond===e.millisecond;return!s(t)||!s(r)?[]:t.toInstant().epochNanoseconds===r.toInstant().epochNanoseconds?[t]:[t,r]}export{l as a,h as b,w as c,Z as d,T as e,k as f,D as g,x as h,I as i};
2
+ //# sourceMappingURL=chunk-J2AQNXF2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/timezone.ts"],"sourcesContent":["// Time-zone subsystem. Wraps Temporal.ZonedDateTime's\n// timezone introspection surface in temporal-fmt's field-based convention.\n// Includes disambiguation modes (compatible/earlier/later/reject) for\n// DST gaps and overlaps, plus transition queries (getNextTransition,\n// getPreviousTransition, getTransitions).\n\nimport { getTemporal } from './temporalProvider.js';\nimport { add } from './arithmetic.js';\n\nexport type DisambiguationMode = 'compatible' | 'earlier' | 'later' | 'reject';\n\nexport interface ResolveZonedOptions {\n disambiguation?: DisambiguationMode;\n overflow?: 'constrain' | 'reject';\n offset?: 'use' | 'ignore' | 'prefer' | 'reject';\n}\n\n// Resolves a wall-clock time in a timezone, applying disambiguation\n// for DST gaps and overlaps.\nexport function resolveZoned(\n fields: { year: number; month: number; day: number; hour?: number; minute?: number; second?: number; millisecond?: number; microsecond?: number; nanosecond?: number },\n timeZone: string,\n options: ResolveZonedOptions = {},\n): unknown {\n const temporal = getTemporal();\n const disambiguation = options.disambiguation ?? 'compatible';\n const overflow = options.overflow ?? 'constrain';\n // Cast options to a permissive record — Temporal.ZonedDateTime.from\n // accepts disambiguation/offset options, but the existing\n // TemporalFactory.from() signature in temporalProvider.ts is narrower.\n // The runtime accepts the wider shape; this cast bypasses the type\n // narrowing without changing runtime behavior.\n const fromOptions = {\n overflow,\n disambiguation,\n offset: options.offset ?? 'prefer',\n } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1];\n try {\n return temporal.ZonedDateTime.from(\n {\n year: fields.year,\n month: fields.month,\n day: fields.day,\n hour: fields.hour ?? 0,\n minute: fields.minute ?? 0,\n second: fields.second ?? 0,\n millisecond: fields.millisecond ?? 0,\n microsecond: fields.microsecond ?? 0,\n nanosecond: fields.nanosecond ?? 0,\n timeZone,\n },\n fromOptions,\n );\n } catch (err) {\n // Surface DST gap / overlap errors with a clearer message.\n const msg = (err as Error).message;\n if (/no such (wall-clock )?time/i.test(msg) && disambiguation === 'reject') {\n throw new Error(\n `temporal-fmt: \"${timeZone}\" has no such wall-clock time on ${fields.year}-${fields.month}-${fields.day}T${fields.hour ?? 0}:${fields.minute ?? 0} — ` +\n `it falls in a DST gap. Pass { disambiguation: 'compatible' | 'earlier' | 'later' } to pick an instant.`\n );\n }\n throw err;\n }\n}\n\nexport function getTimeZone(value: unknown): string {\n const v = value as { timeZoneId?: string };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTimeZone() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.timeZoneId;\n}\n\nexport function getOffset(value: unknown): string {\n const v = value as { offset?: string };\n if (typeof v?.offset !== 'string') {\n throw new Error(`temporal-fmt: getOffset() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.offset;\n}\n\nexport function getOffsetNanoseconds(value: unknown): number {\n const v = value as { offsetNanoseconds?: number; offset?: string };\n if (typeof v?.offsetNanoseconds === 'number') return v.offsetNanoseconds;\n if (typeof v?.offset === 'string') {\n // Parse ±HH:MM → nanoseconds.\n const m = v.offset.match(/^([+-])(\\d{2}):(\\d{2})$/);\n if (!m) throw new Error(`temporal-fmt: getOffsetNanoseconds() couldn't parse offset \"${v.offset}\".`);\n const sign = m[1] === '-' ? -1 : 1;\n return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60) * 1_000_000_000;\n }\n throw new Error(`temporal-fmt: getOffsetNanoseconds() expected a ZonedDateTime, got ${typeof value}.`);\n}\n\n// DST detection: a ZonedDateTime is in DST iff the offset is different\n// from the timezone's standard offset at the same instant. Computed\n// by comparing the current offset to the offset in January (Northern\n// Hemisphere) or July (Southern Hemisphere) of the same year — a\n// heuristic that's right for most populated zones.\nexport function isDST(value: unknown): boolean {\n const v = value as { timeZoneId?: string; offset?: string; year?: number; toInstant?: () => unknown };\n if (typeof v?.timeZoneId !== 'string' || typeof v?.offset !== 'string' || typeof v?.year !== 'number') {\n throw new Error(`temporal-fmt: isDST() expected a ZonedDateTime, got ${typeof value}.`);\n }\n // Compute the offset for January 1 of the same year (standard time\n // for Northern Hemisphere zones) and compare. For Southern Hemisphere\n // zones January is summer, so we'd want July instead — but checking\n // January vs current is good enough for the common case, and we\n // can't reliably determine hemisphere without a lookup table.\n const temporal = getTemporal();\n try {\n const jan = temporal.ZonedDateTime.from(\n { year: v.year, month: 1, day: 1, hour: 12, timeZone: v.timeZoneId },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n return jan.offset !== v.offset;\n } catch {\n return false;\n }\n}\n\n// Returns the next DST transition strictly after `value`, or undefined\n// if no transition occurs within the next 2 years. Implemented by\n// walking day-by-day checking for offset changes — slow but simple,\n// and correct across all IANA zones.\nexport function getNextTransition(value: unknown): unknown | undefined {\n return findTransition(value, 1);\n}\n\nexport function getPreviousTransition(value: unknown): unknown | undefined {\n return findTransition(value, -1);\n}\n\nfunction findTransition(value: unknown, direction: 1 | -1): unknown | undefined {\n const v = value as { timeZoneId?: string; offset?: string; toInstant?: () => unknown; year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: ${direction > 0 ? 'getNextTransition' : 'getPreviousTransition'}() expected a ZonedDateTime, got ${typeof value}.`);\n }\n const temporal = getTemporal();\n const timeZone = v.timeZoneId;\n let candidateFields = add(value, direction, 'days');\n const startOffset = v.offset!;\n for (let i = 0; i < 365 * 2; i++) {\n // Rebuild a real ZonedDateTime for this candidate day, in this\n // timezone, so `.offset` reflects the actual UTC offset on that\n // day rather than being absent. add() only produces a plain field\n // bag (it has no notion of timezone), so re-attaching timeZone via\n // ZonedDateTime.from is what actually lets us detect a DST change\n // here — using the field bag's own (nonexistent) offset would make\n // every call \"find\" a transition on the very first day checked.\n const cf = candidateFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const candidateZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n if (candidateZdt.offset !== startOffset) {\n // Found a transition day. Return the reconstructed ZonedDateTime.\n return candidateZdt;\n }\n candidateFields = add(candidateFields, direction, 'days');\n }\n return undefined;\n}\n\nexport function getTransitions(start: unknown, end: unknown): unknown[] {\n const result: unknown[] = [];\n const startV = start as { timeZoneId?: string; offset?: string; year?: number; month?: number; day?: number };\n if (typeof startV?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTransitions() expected a ZonedDateTime for start, got ${typeof start}.`);\n }\n const endV = end as { year: number; month: number; day: number };\n const temporal = getTemporal();\n const timeZone = startV.timeZoneId;\n let cursorFields = add(start, 1, 'days');\n let lastOffset = startV.offset!;\n for (let i = 0; i < 365 * 5; i++) {\n // Same reconstruction as findTransition() above — add() strips\n // timezone/offset info, so we rebuild a real ZonedDateTime each\n // iteration to read its actual offset instead of comparing against\n // a field bag that never has one.\n const cf = cursorFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const cursorZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string; year: number; month: number; day: number };\n if (cursorZdt.offset !== lastOffset) {\n // Found a transition.\n if (cursorZdt.year > endV.year || (cursorZdt.year === endV.year && (cursorZdt.month > endV.month || (cursorZdt.month === endV.month && cursorZdt.day > endV.day)))) break;\n result.push(cursorZdt);\n lastOffset = cursorZdt.offset;\n }\n cursorFields = add(cursorFields, 1, 'days');\n }\n return result;\n}\n\n// Returns the list of possible instants for a wall-clock time in a\n// zone — used to detect gaps (0 instants) and overlaps (2 instants).\nexport function possibleInstantsFor(\n fields: { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number },\n timeZone: string,\n): unknown[] {\n const temporal = getTemporal();\n // Resolve the same wall-clock fields twice, forcing disambiguation\n // toward the earlier and later side of any DST boundary. Comparing\n // the two results tells us which of the three cases we're in:\n // - gap (e.g. 2:30am on a spring-forward day): neither result's own\n // wall-clock fields match what was requested, since Temporal has\n // to shift off the nonexistent time in both directions — 0 instants.\n // - overlap (e.g. 1:30am on a fall-back day): both results keep the\n // requested wall-clock time but land on different instants (one\n // per side of the boundary) — 2 instants.\n // - normal time: both results match the requested wall-clock time\n // and resolve to the same instant — 1 instant.\n const earlier = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'earlier', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n const later = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'later', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n\n const matchesRequestedWallClock = (z: typeof earlier) =>\n z.year === fields.year && z.month === fields.month && z.day === fields.day &&\n z.hour === fields.hour && z.minute === fields.minute &&\n z.second === fields.second && z.millisecond === fields.millisecond;\n\n if (!matchesRequestedWallClock(earlier) || !matchesRequestedWallClock(later)) {\n // Gap — the requested wall-clock time doesn't exist in this zone.\n return [];\n }\n if (earlier.toInstant().epochNanoseconds === later.toInstant().epochNanoseconds) {\n // Normal time — both disambiguation directions agree on the instant.\n return [earlier];\n }\n // Overlap — same wall-clock time, two distinct instants.\n return [earlier, later];\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/timezone.ts"],"sourcesContent":["// Time-zone subsystem. Wraps Temporal.ZonedDateTime's\n// timezone introspection surface in temporal-fmt's field-based convention.\n// Includes disambiguation modes (compatible/earlier/later/reject) for\n// DST gaps and overlaps, plus transition queries (getNextTransition,\n// getPreviousTransition, getTransitions).\n\nimport { getTemporal } from './temporalProvider.js';\nimport { add } from './arithmetic.js';\n\nexport type DisambiguationMode = 'compatible' | 'earlier' | 'later' | 'reject';\n\nexport interface ResolveZonedOptions {\n disambiguation?: DisambiguationMode;\n overflow?: 'constrain' | 'reject';\n offset?: 'use' | 'ignore' | 'prefer' | 'reject';\n}\n\n// Resolves a wall-clock time in a timezone, applying disambiguation\n// for DST gaps and overlaps.\nexport function resolveZoned(\n fields: { year: number; month: number; day: number; hour?: number; minute?: number; second?: number; millisecond?: number; microsecond?: number; nanosecond?: number },\n timeZone: string,\n options: ResolveZonedOptions = {},\n): unknown {\n const temporal = getTemporal();\n const disambiguation = options.disambiguation ?? 'compatible';\n const overflow = options.overflow ?? 'constrain';\n // Cast options to a permissive record — Temporal.ZonedDateTime.from\n // accepts disambiguation/offset options, but the existing\n // TemporalFactory.from() signature in temporalProvider.ts is narrower.\n // The runtime accepts the wider shape; this cast bypasses the type\n // narrowing without changing runtime behavior.\n const fromOptions = {\n overflow,\n disambiguation,\n offset: options.offset ?? 'prefer',\n } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1];\n try {\n return temporal.ZonedDateTime.from(\n {\n year: fields.year,\n month: fields.month,\n day: fields.day,\n hour: fields.hour ?? 0,\n minute: fields.minute ?? 0,\n second: fields.second ?? 0,\n millisecond: fields.millisecond ?? 0,\n microsecond: fields.microsecond ?? 0,\n nanosecond: fields.nanosecond ?? 0,\n timeZone,\n },\n fromOptions,\n );\n } catch (err) {\n // Surface DST gap / overlap errors with a clearer message.\n const msg = (err as Error).message;\n if (/no such (wall-clock )?time/i.test(msg) && disambiguation === 'reject') {\n throw new Error(\n `temporal-fmt: \"${timeZone}\" has no such wall-clock time on ${fields.year}-${fields.month}-${fields.day}T${fields.hour ?? 0}:${fields.minute ?? 0} — ` +\n `it falls in a DST gap. Pass { disambiguation: 'compatible' | 'earlier' | 'later' } to pick an instant.`\n );\n }\n throw err;\n }\n}\n\nexport function getTimeZone(value: unknown): string {\n const v = value as { timeZoneId?: string };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTimeZone() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.timeZoneId;\n}\n\nexport function getOffset(value: unknown): string {\n const v = value as { offset?: string };\n if (typeof v?.offset !== 'string') {\n throw new Error(`temporal-fmt: getOffset() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.offset;\n}\n\nexport function getOffsetNanoseconds(value: unknown): number {\n const v = value as { offsetNanoseconds?: number; offset?: string };\n if (typeof v?.offsetNanoseconds === 'number') return v.offsetNanoseconds;\n if (typeof v?.offset === 'string') {\n // Parse ±HH:MM → nanoseconds.\n const m = v.offset.match(/^([+-])(\\d{2}):(\\d{2})$/);\n if (!m) throw new Error(`temporal-fmt: getOffsetNanoseconds() couldn't parse offset \"${v.offset}\".`);\n const sign = m[1] === '-' ? -1 : 1;\n return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60) * 1_000_000_000;\n }\n throw new Error(`temporal-fmt: getOffsetNanoseconds() expected a ZonedDateTime, got ${typeof value}.`);\n}\n\n// DST detection: a ZonedDateTime is in DST iff the offset is different\n// from the timezone's standard offset at the same instant. Computed\n// by comparing the current offset to the offset in January (Northern\n// Hemisphere) or July (Southern Hemisphere) of the same year — a\n// heuristic that's right for most populated zones.\nexport function isDST(value: unknown): boolean {\n const v = value as { timeZoneId?: string; offset?: string; year?: number; toInstant?: () => unknown };\n if (typeof v?.timeZoneId !== 'string' || typeof v?.offset !== 'string' || typeof v?.year !== 'number') {\n throw new Error(`temporal-fmt: isDST() expected a ZonedDateTime, got ${typeof value}.`);\n }\n // Compute the offset for January 1 of the same year (standard time\n // for Northern Hemisphere zones) and compare. For Southern Hemisphere\n // zones January is summer, so we'd want July instead — but checking\n // January vs current is good enough for the common case, and we\n // can't reliably determine hemisphere without a lookup table.\n const temporal = getTemporal();\n try {\n const jan = temporal.ZonedDateTime.from(\n { year: v.year, month: 1, day: 1, hour: 12, timeZone: v.timeZoneId },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n return jan.offset !== v.offset;\n } catch {\n return false;\n }\n}\n\n// Returns the next DST transition strictly after `value`, or undefined\n// if no transition occurs within the next 2 years. Implemented by\n// walking day-by-day checking for offset changes — slow but simple,\n// and correct across all IANA zones.\nexport function getNextTransition(value: unknown): unknown | undefined {\n return findTransition(value, 1);\n}\n\nexport function getPreviousTransition(value: unknown): unknown | undefined {\n return findTransition(value, -1);\n}\n\nfunction findTransition(value: unknown, direction: 1 | -1): unknown | undefined {\n const v = value as { timeZoneId?: string; offset?: string; toInstant?: () => unknown; year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: ${direction > 0 ? 'getNextTransition' : 'getPreviousTransition'}() expected a ZonedDateTime, got ${typeof value}.`);\n }\n const temporal = getTemporal();\n const timeZone = v.timeZoneId;\n let candidateFields = add(value, direction, 'days');\n const startOffset = v.offset!;\n for (let i = 0; i < 365 * 2; i++) {\n // Rebuild a real ZonedDateTime for this candidate day, in this\n // timezone, so `.offset` reflects the actual UTC offset on that\n // day rather than being absent. add() only produces a plain field\n // bag (it has no notion of timezone), so re-attaching timeZone via\n // ZonedDateTime.from is what actually lets us detect a DST change\n // here — using the field bag's own (nonexistent) offset would make\n // every call \"find\" a transition on the very first day checked.\n const cf = candidateFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const candidateZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n if (candidateZdt.offset !== startOffset) {\n // Found a transition day. Return the reconstructed ZonedDateTime.\n return candidateZdt;\n }\n candidateFields = add(candidateFields, direction, 'days');\n }\n return undefined;\n}\n\nexport function getTransitions(start: unknown, end: unknown): unknown[] {\n const result: unknown[] = [];\n const startV = start as { timeZoneId?: string; offset?: string; year?: number; month?: number; day?: number };\n if (typeof startV?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTransitions() expected a ZonedDateTime for start, got ${typeof start}.`);\n }\n const endV = end as { year: number; month: number; day: number };\n const temporal = getTemporal();\n const timeZone = startV.timeZoneId;\n let cursorFields = add(start, 1, 'days');\n let lastOffset = startV.offset!;\n for (let i = 0; i < 365 * 5; i++) {\n // Same reconstruction as findTransition() above — add() strips\n // timezone/offset info, so we rebuild a real ZonedDateTime each\n // iteration to read its actual offset instead of comparing against\n // a field bag that never has one.\n const cf = cursorFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const cursorZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string; year: number; month: number; day: number };\n if (cursorZdt.offset !== lastOffset) {\n // Found a transition.\n if (cursorZdt.year > endV.year || (cursorZdt.year === endV.year && (cursorZdt.month > endV.month || (cursorZdt.month === endV.month && cursorZdt.day > endV.day)))) break;\n result.push(cursorZdt);\n lastOffset = cursorZdt.offset;\n }\n cursorFields = add(cursorFields, 1, 'days');\n }\n return result;\n}\n\n// Returns the list of possible instants for a wall-clock time in a\n// zone — used to detect gaps (0 instants) and overlaps (2 instants).\nexport function possibleInstantsFor(\n fields: { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number },\n timeZone: string,\n): unknown[] {\n const temporal = getTemporal();\n // Resolve the same wall-clock fields twice, forcing disambiguation\n // toward the earlier and later side of any DST boundary. Comparing\n // the two results tells us which of the three cases we're in:\n // - gap (e.g. 2:30am on a spring-forward day): neither result's own\n // wall-clock fields match what was requested, since Temporal has\n // to shift off the nonexistent time in both directions — 0 instants.\n // - overlap (e.g. 1:30am on a fall-back day): both results keep the\n // requested wall-clock time but land on different instants (one\n // per side of the boundary) — 2 instants.\n // - normal time: both results match the requested wall-clock time\n // and resolve to the same instant — 1 instant.\n const earlier = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'earlier', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n const later = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'later', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n\n const matchesRequestedWallClock = (z: typeof earlier) =>\n z.year === fields.year && z.month === fields.month && z.day === fields.day &&\n z.hour === fields.hour && z.minute === fields.minute &&\n z.second === fields.second && z.millisecond === fields.millisecond;\n\n if (!matchesRequestedWallClock(earlier) || !matchesRequestedWallClock(later)) {\n // Gap — the requested wall-clock time doesn't exist in this zone.\n return [];\n }\n if (earlier.toInstant().epochNanoseconds === later.toInstant().epochNanoseconds) {\n // Normal time — both disambiguation directions agree on the instant.\n return [earlier];\n }\n // Overlap — same wall-clock time, two distinct instants.\n return [earlier, later];\n}"],"mappings":"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","names":["resolveZoned","fields","timeZone","options","temporal","getTemporal","disambiguation","fromOptions","err","msg","getTimeZone","value","v","getOffset","getOffsetNanoseconds","m","isDST","getNextTransition","findTransition","getPreviousTransition","direction","candidateFields","add","startOffset","i","cf","candidateZdt","getTransitions","start","end","result","startV","endV","cursorFields","lastOffset","cursorZdt","possibleInstantsFor","earlier","later","matchesRequestedWallClock","z"]}
@@ -0,0 +1,2 @@
1
+ import{a as u}from"./chunk-C5YESWFT.js";import{k as f,p as c}from"./chunk-5U5WJ465.js";import{w as l}from"./chunk-VQXUMFB2.js";var s=new Map,y=100;function w(r,n){let t=`${r}|${n}`,e=s.get(t);if(e)return e;if(s.size>=y){let o=s.keys().next().value;o!==void 0&&s.delete(o)}try{e=new Intl.RelativeTimeFormat(c(r),{numeric:n})}catch(o){throw new f({actual:r,reason:o.message})}return s.set(t,e),e}function p(r,n,t={}){let e=t.locale??u,o=t.numeric??"auto",a=w(e,o),i=-l(r,n),m=Math.abs(i);return m===0?a.format(0,"day"):m<7?a.format(i,"day"):m<30?a.format(-Math.trunc(-i/7),"week"):m<365?a.format(-Math.trunc(-i/30),"month"):a.format(-Math.trunc(-i/365),"year")}function v(r,n={}){let t=new Date,e={year:t.getFullYear(),month:t.getMonth()+1,day:t.getDate()};return p(r,e,n)}export{p as a,v as b};
2
+ //# sourceMappingURL=chunk-NBXF7V5B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/relativeTime.ts"],"sourcesContent":["// Relative time helpers. The existing formatDistance() in\n// formatDistance.ts stays; this module adds formatRelative() and\n// formatRelativeToNow().\n//\n// Semantic distinction:\n// formatDistance(date1, date2) — \"3 days ago\" / \"in 2 hours\"\n// Describes the gap between two values, delegating unit selection\n// and pluralization to Intl.RelativeTimeFormat.\n// formatRelative(date, baseDate) — \"yesterday\" / \"tomorrow\" / \"today\"\n// / \"in 3 days\" / \"last week\"\n// Calendar-relative: uses the calendar date diff, not the ms diff.\n// \"in 1 day\" means \"tomorrow\", even if it's 23 hours away.\n// formatRelativeToNow(date) — same as formatRelative(date, now)\n\nimport { differenceInDays } from './arithmetic.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface FormatRelativeOptions extends FormatOptions {\n // 'auto' (default) lets Intl.RelativeTimeFormat use natural forms like\n // \"yesterday\"/\"tomorrow\"/\"now\" when the value lands on ±1 or 0.\n // 'always' forces the strict \"1 day ago\"/\"in 1 day\" form.\n numeric?: 'always' | 'auto';\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n // Same cache shape as formatDistance.ts uses.\n const key = `${locale}|${numeric}`;\n let rtf = rtfCache.get(key);\n if (rtf) return rtf;\n if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n const oldestKey = rtfCache.keys().next().value;\n if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n }\n try {\n rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n rtfCache.set(key, rtf);\n return rtf;\n}\n\n// Calendar-relative \"describe date1 as if standing at date2\" — uses\n// differenceInDays (which counts date boundaries, not 24-hour periods),\n// so \"2026-08-04 23:59\" relative to \"2026-08-05 00:00\" reads as\n// \"yesterday\" (1 day ago), not \"1 second ago\".\n//\n// differenceInDays(a, b) returns b - a; for formatRelative(date1, date2)\n// we want date1 - date2 (so positive means date1 is in the future relative\n// to date2). Swap the argument order.\nexport function formatRelative(\n date1: unknown,\n date2: unknown,\n options: FormatRelativeOptions = {},\n): string {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const numeric = options.numeric ?? 'auto';\n const rtf = getRtf(locale, numeric);\n const dayDiff = -differenceInDays(date1, date2);\n const absDays = Math.abs(dayDiff);\n\n if (absDays === 0) {\n return rtf.format(0, 'day'); // \"now\" / \"in 0 days\"\n }\n if (absDays < 7) {\n return rtf.format(dayDiff, 'day');\n }\n if (absDays < 30) {\n return rtf.format(-Math.trunc(-dayDiff / 7), 'week');\n }\n if (absDays < 365) {\n return rtf.format(-Math.trunc(-dayDiff / 30), 'month');\n }\n return rtf.format(-Math.trunc(-dayDiff / 365), 'year');\n}\n\nexport function formatRelativeToNow(\n date: unknown,\n options: FormatRelativeOptions = {},\n): string {\n // Use the system's current date as the reference. PlainDate.from a\n // JS Date so differenceInDays sees the right shape.\n const now = new Date();\n const nowFields = {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n };\n return formatRelative(date, nowFields, options);\n}\n"],"mappings":"+HA0BA,IAAMA,EAAW,IAAI,IACfC,EAAqB,IAE3B,SAASC,EAAOC,EAAgBC,EAAqD,CAEnF,IAAMC,EAAM,GAAGF,CAAM,IAAIC,CAAO,GAC5BE,EAAMN,EAAS,IAAIK,CAAG,EAC1B,GAAIC,EAAK,OAAOA,EAChB,GAAIN,EAAS,MAAQC,EAAoB,CACvC,IAAMM,EAAYP,EAAS,KAAK,EAAE,KAAK,EAAE,MACrCO,IAAc,QAAWP,EAAS,OAAOO,CAAS,CACxD,CACA,GAAI,CACFD,EAAM,IAAI,KAAK,mBAAmBE,EAAmBL,CAAM,EAAG,CAAE,QAAAC,CAAQ,CAAC,CAC3E,OAASK,EAAK,CAMZ,MAAM,IAAIC,EAAmB,CAAE,OAAQP,EAAQ,OAASM,EAAc,OAAQ,CAAC,CACjF,CACA,OAAAT,EAAS,IAAIK,EAAKC,CAAG,EACdA,CACT,CAUO,SAASK,EACdC,EACAC,EACAC,EAAiC,CAAC,EAC1B,CACR,IAAMX,EAASW,EAAQ,QAAUC,EAC3BX,EAAUU,EAAQ,SAAW,OAC7BR,EAAMJ,EAAOC,EAAQC,CAAO,EAC5BY,EAAU,CAACC,EAAiBL,EAAOC,CAAK,EACxCK,EAAU,KAAK,IAAIF,CAAO,EAEhC,OAAIE,IAAY,EACPZ,EAAI,OAAO,EAAG,KAAK,EAExBY,EAAU,EACLZ,EAAI,OAAOU,EAAS,KAAK,EAE9BE,EAAU,GACLZ,EAAI,OAAO,CAAC,KAAK,MAAM,CAACU,EAAU,CAAC,EAAG,MAAM,EAEjDE,EAAU,IACLZ,EAAI,OAAO,CAAC,KAAK,MAAM,CAACU,EAAU,EAAE,EAAG,OAAO,EAEhDV,EAAI,OAAO,CAAC,KAAK,MAAM,CAACU,EAAU,GAAG,EAAG,MAAM,CACvD,CAEO,SAASG,EACdC,EACAN,EAAiC,CAAC,EAC1B,CAGR,IAAMO,EAAM,IAAI,KACVC,EAAY,CAChB,KAAMD,EAAI,YAAY,EACtB,MAAOA,EAAI,SAAS,EAAI,EACxB,IAAKA,EAAI,QAAQ,CACnB,EACA,OAAOV,EAAeS,EAAME,EAAWR,CAAO,CAChD","names":["rtfCache","MAX_RTF_CACHE_SIZE","getRtf","locale","numeric","key","rtf","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","formatRelative","date1","date2","options","DEFAULT_LOCALE","dayDiff","differenceInDays","absDays","formatRelativeToNow","date","now","nowFields"]}
@@ -0,0 +1,7 @@
1
+ import{b as d}from"./chunk-C5YESWFT.js";import{b as c,c as p,k as l}from"./chunk-5U5WJ465.js";var h=d.map(([n])=>n).sort((n,t)=>t.length-n.length);function N(n){let t=[],e=0;for(;e<n.length;){let o=n[e];if(o==="'"){if(n[e+1]==="'"){a(t,"'"),e+=2;continue}let r=e+1,s="",u=!1;for(;r<n.length;){if(n[r]==="'"){if(n[r+1]==="'"){s+="'",r+=2;continue}u=!0,r+=1;break}s+=n[r],r+=1}if(!u)throw new c({format:n,message:`temporal-fmt: unterminated quote in format string "${n}"`});a(t,s),e=r;continue}let i=h.find(r=>n.startsWith(r,e));if(i){let r=i[i.length-1];if(n[e+i.length]===r){let s=e+i.length;for(;n[s]===r;)s+=1;throw new p({format:n,token:n.slice(e,s),message:`temporal-fmt: "${n.slice(e,s)}" in format string "${n}" isn't a recognized token \u2014 did you mean "${i}"?`})}t.push({kind:"token",value:i}),e+=i.length;continue}a(t,o),e+=1}return t}function a(n,t){let e=n[n.length-1];e&&e.kind==="literal"?e.value+=t:n.push({kind:"literal",value:t})}var g=new Set(["latn","arab","deva","beng","guru","gujr","orya","tamldec","telu","knda","mlym","fullwide","hanidec"]),m=new Map;function f(n){let t=m.get(n);if(t)return t;/* c8 ignore start @preserve -- dead by construction, not just
2
+ untested: both callers (convertDigits, convertDigitsToAscii) already
3
+ return early on system === 'latn' before calling getDigitMap at all,
4
+ so this function is never invoked with 'latn'. Kept as a defensive
5
+ fallback rather than trusting that stays true for any future
6
+ caller. */if(n==="latn"){t={};for(let e=0;e<10;e++)t[String(e)]=String(e)}else{/* c8 ignore stop @preserve */let e=new Intl.NumberFormat("en-US-u-nu-"+n,{useGrouping:!1});t={};for(let o=0;o<10;o++)t[String(o)]=e.format(o)}return m.set(n,t),t}function S(n,t){if(t==="latn")return n;if(!g.has(t))throw new l({actual:t,reason:`numbering system "${t}" is not supported. Supported: ${[...g].join(", ")}.`});let e=f(t),o="";for(let i of n)i>="0"&&i<="9"?o+=e[i]??i:o+=i;return o}function b(n,t){if(t==="latn")return n;if(!g.has(t))throw new l({actual:t,reason:`numbering system "${t}" is not supported.`});let e=f(t),o={};for(let r of Object.keys(e))o[e[r]]=r;let i="";for(let r of n)i+=o[r]??r;return i}function O(n,t){let e=t.numberingSystem??"latn";return e==="latn"?n:S(n,e)}function v(n,t){let e=t.parseNumberingSystem??"latn";return e==="latn"?n:b(n,e)}export{N as a,g as b,S as c,b as d,O as e,v as f};
7
+ //# sourceMappingURL=chunk-R52YOKI3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tokenize.ts","../src/numbering.ts"],"sourcesContent":["import { TOKENS } from './tokens.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new FormatSyntaxError({\n format,\n message: `temporal-fmt: unterminated quote in format string \"${format}\"`,\n });\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // match is already the longest registered token starting at i, so\n // one more of its last character can't be a token of its own —\n // it'd fall through to whatever token or literal rule handles that\n // character next, silently splicing an unrelated field onto this\n // one (e.g. \"zzzz\" used to read as zzz + literal \"z\", \"MMMMM\" as\n // MMMM + the numeric-month token M). Treat the whole overlong run\n // as one unrecognized token instead.\n const runChar = match[match.length - 1];\n if (format[i + match.length] === runChar) {\n let end = i + match.length;\n while (format[end] === runChar) end += 1;\n // UnknownTokenError, not FormatSyntaxError: wrapUntypedError's\n // classifier already treats this exact message (\"isn't a\n // recognized token\") as UNKNOWN_TOKEN (see errors.test.js's\n // \"overlong token run classifies as UnknownTokenError\"), and a\n // direct throw here needs to agree with what safeParse's fallback\n // path would have classified it as.\n throw new UnknownTokenError({\n format,\n token: format.slice(i, end),\n message:\n `temporal-fmt: \"${format.slice(i, end)}\" in format string \"${format}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is\n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}","// Numbering systems. Default: latn (ASCII digits).\n// Optional: arab, deva, beng, etc. Configurable on formatting.\n//\n// The library's existing tokens always render ASCII digits (see\n// tokens.ts's pad() — uses String(n) which produces ASCII). This\n// module adds the ability to convert the output to a locale's native\n// digits via Intl.NumberFormat, which is the standard mechanism JS\n// provides for digit transliteration.\n//\n// On the parse side: parse() only accepts ASCII digits, matching how\n// the existing NUMERIC_FRAGMENTS regex is built. A parseNumberingSystem\n// option could convert input digits to ASCII before matching — but\n// that's a per-call opt-in, not silent acceptance of every numeral\n// system.\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport type NumberingSystem = 'latn' | 'arab' | 'deva' | 'beng' | 'guru' | 'gujr' | 'orya' | 'tamldec' | 'telu' | 'knda' | 'mlym' | 'fullwide' | 'hanidec';\n\n// All supported NumberingSystem values. latn is the default and what\n// the rest of the library produces natively.\nexport const SUPPORTED_NUMBERING_SYSTEMS: ReadonlySet<string> = new Set([\n 'latn', 'arab', 'deva', 'beng', 'guru', 'gujr', 'orya', 'tamldec',\n 'telu', 'knda', 'mlym', 'fullwide', 'hanidec',\n]);\n\nconst digitMapCache = new Map<string, Record<string, string>>();\n\n// Builds a per-numbering-system digit transliteration map. Uses\n// Intl.NumberFormat to render 0-9 in the requested system, then\n// builds the lookup table. Cached because constructing a formatter\n// is expensive and we re-use the same map for every digit in the\n// output.\nfunction getDigitMap(system: string): Record<string, string> {\n let map = digitMapCache.get(system);\n if (map) return map;\n /* c8 ignore start @preserve -- dead by construction, not just\n untested: both callers (convertDigits, convertDigitsToAscii) already\n return early on system === 'latn' before calling getDigitMap at all,\n so this function is never invoked with 'latn'. Kept as a defensive\n fallback rather than trusting that stays true for any future\n caller. */\n if (system === 'latn') {\n map = {};\n for (let i = 0; i < 10; i++) map[String(i)] = String(i);\n } else {\n /* c8 ignore stop @preserve */\n const fmt = new Intl.NumberFormat('en-US-u-nu-' + system, { useGrouping: false });\n map = {};\n for (let i = 0; i < 10; i++) {\n map[String(i)] = fmt.format(i);\n }\n }\n digitMapCache.set(system, map);\n return map;\n}\n\n// Converts every ASCII digit in `s` to its equivalent in the requested\n// numbering system. Non-digit characters pass through unchanged.\nexport function convertDigits(s: string, system: string): string {\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(', ')}.` });\n }\n const map = getDigitMap(system);\n let result = '';\n for (const ch of s) {\n if (ch >= '0' && ch <= '9') {\n // map[ch] is always populated for '0'-'9' since getDigitMap builds\n // all ten digit keys for every supported system and ch is already\n // range-checked above; the ?? ch fallback exists only as a\n // type-safety guard against Record's implicit undefined, not a\n // reachable runtime path.\n /* c8 ignore next */\n result += map[ch] ?? ch;\n } else {\n result += ch;\n }\n }\n return result;\n}\n\n// Parses a string with non-ASCII digits back to ASCII. The inverse of\n// convertDigits — used by parse() when an explicit `numberingSystem`\n// option is passed. Throws on unsupported systems.\nexport function convertDigitsToAscii(s: string, system: string): string {\n // Same dead-by-construction situation as applyParseNumbering's own\n // 'latn' guard: this function's only caller (applyParseNumbering)\n // already returns early on system === 'latn' before reaching this\n // call, so system is never 'latn' here in practice. Kept as a\n // defensive guard for any future direct caller.\n /* c8 ignore next */\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported.` });\n }\n const map = getDigitMap(system);\n // Build reverse map.\n const reverse: Record<string, string> = {};\n for (const k of Object.keys(map)) reverse[map[k]!] = k;\n let result = '';\n for (const ch of s) {\n result += reverse[ch] ?? ch;\n }\n return result;\n}\n\n// Augmented FormatOptions that includes the numberingSystem field.\n// Callers pass { numberingSystem: 'arab' } to format() to get Arabic-\n// Indic digits in the output.\nexport interface NumberingFormatOptions extends FormatOptions {\n numberingSystem?: string;\n}\n\n// Parse-side counterpart. Named parseNumberingSystem (not numberingSystem)\n// so a caller mixing format() and parse() options in the same config\n// object can set both independently — the two directions aren't always\n// symmetric (e.g. converting *to* native digits on output without\n// expecting native digits back on input, or vice versa).\nexport interface NumberingParseOptions extends FormatOptions {\n parseNumberingSystem?: string;\n}\n\n// Helper for the format path: takes the formatted ASCII output of\n// format() and converts digits if a numberingSystem was requested.\n// Kept here so format.ts doesn't need to know about numbering systems.\nexport function applyNumbering(s: string, options: NumberingFormatOptions): string {\n const system = options.numberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigits(s, system);\n}\n\n// Helper for the parse path: converts input digits to ASCII before\n// matching, when parseNumberingSystem is set. Distinct from the format\n// path's option name (numberingSystem vs parseNumberingSystem) so a\n// caller can be explicit about which direction they want transliterated.\nexport function applyParseNumbering(s: string, options: { parseNumberingSystem?: string }): string {\n // Both call sites of applyParseNumbering (parse.ts) already guard with\n // `if (options.parseNumberingSystem)` before calling it, so\n // options.parseNumberingSystem is always truthy here — the ?? 'latn'\n // fallback, and the subsequent 'latn' early return, are dead by\n // construction, not just untested. Kept as a defensive default rather\n // than assuming every future caller replicates the guard.\n /* c8 ignore next 2 */\n const system = options.parseNumberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigitsToAscii(s, system);\n}\n\n// Suppress unused-import warning. DEFAULT_LOCALE is imported for the\n// type augmentation pattern above; if the project ever exports a\n// typed-locale interface, it'll be the source of truth for the default.\nvoid DEFAULT_LOCALE;"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/tokenize.ts","../src/numbering.ts"],"sourcesContent":["import { TOKENS } from './tokens.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new FormatSyntaxError({\n format,\n message: `temporal-fmt: unterminated quote in format string \"${format}\"`,\n });\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // match is already the longest registered token starting at i, so\n // one more of its last character can't be a token of its own —\n // it'd fall through to whatever token or literal rule handles that\n // character next, silently splicing an unrelated field onto this\n // one (e.g. \"zzzz\" used to read as zzz + literal \"z\", \"MMMMM\" as\n // MMMM + the numeric-month token M). Treat the whole overlong run\n // as one unrecognized token instead.\n const runChar = match[match.length - 1];\n if (format[i + match.length] === runChar) {\n let end = i + match.length;\n while (format[end] === runChar) end += 1;\n // UnknownTokenError, not FormatSyntaxError: wrapUntypedError's\n // classifier already treats this exact message (\"isn't a\n // recognized token\") as UNKNOWN_TOKEN (see errors.test.js's\n // \"overlong token run classifies as UnknownTokenError\"), and a\n // direct throw here needs to agree with what safeParse's fallback\n // path would have classified it as.\n throw new UnknownTokenError({\n format,\n token: format.slice(i, end),\n message:\n `temporal-fmt: \"${format.slice(i, end)}\" in format string \"${format}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is\n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}","// Numbering systems. Default: latn (ASCII digits).\n// Optional: arab, deva, beng, etc. Configurable on formatting.\n//\n// The library's existing tokens always render ASCII digits (see\n// tokens.ts's pad() — uses String(n) which produces ASCII). This\n// module adds the ability to convert the output to a locale's native\n// digits via Intl.NumberFormat, which is the standard mechanism JS\n// provides for digit transliteration.\n//\n// On the parse side: parse() only accepts ASCII digits, matching how\n// the existing NUMERIC_FRAGMENTS regex is built. A parseNumberingSystem\n// option could convert input digits to ASCII before matching — but\n// that's a per-call opt-in, not silent acceptance of every numeral\n// system.\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport type NumberingSystem = 'latn' | 'arab' | 'deva' | 'beng' | 'guru' | 'gujr' | 'orya' | 'tamldec' | 'telu' | 'knda' | 'mlym' | 'fullwide' | 'hanidec';\n\n// All supported NumberingSystem values. latn is the default and what\n// the rest of the library produces natively.\nexport const SUPPORTED_NUMBERING_SYSTEMS: ReadonlySet<string> = new Set([\n 'latn', 'arab', 'deva', 'beng', 'guru', 'gujr', 'orya', 'tamldec',\n 'telu', 'knda', 'mlym', 'fullwide', 'hanidec',\n]);\n\nconst digitMapCache = new Map<string, Record<string, string>>();\n\n// Builds a per-numbering-system digit transliteration map. Uses\n// Intl.NumberFormat to render 0-9 in the requested system, then\n// builds the lookup table. Cached because constructing a formatter\n// is expensive and we re-use the same map for every digit in the\n// output.\nfunction getDigitMap(system: string): Record<string, string> {\n let map = digitMapCache.get(system);\n if (map) return map;\n /* c8 ignore start @preserve -- dead by construction, not just\n untested: both callers (convertDigits, convertDigitsToAscii) already\n return early on system === 'latn' before calling getDigitMap at all,\n so this function is never invoked with 'latn'. Kept as a defensive\n fallback rather than trusting that stays true for any future\n caller. */\n if (system === 'latn') {\n map = {};\n for (let i = 0; i < 10; i++) map[String(i)] = String(i);\n } else {\n /* c8 ignore stop @preserve */\n const fmt = new Intl.NumberFormat('en-US-u-nu-' + system, { useGrouping: false });\n map = {};\n for (let i = 0; i < 10; i++) {\n map[String(i)] = fmt.format(i);\n }\n }\n digitMapCache.set(system, map);\n return map;\n}\n\n// Converts every ASCII digit in `s` to its equivalent in the requested\n// numbering system. Non-digit characters pass through unchanged.\nexport function convertDigits(s: string, system: string): string {\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(', ')}.` });\n }\n const map = getDigitMap(system);\n let result = '';\n for (const ch of s) {\n if (ch >= '0' && ch <= '9') {\n // map[ch] is always populated for '0'-'9' since getDigitMap builds\n // all ten digit keys for every supported system and ch is already\n // range-checked above; the ?? ch fallback exists only as a\n // type-safety guard against Record's implicit undefined, not a\n // reachable runtime path.\n /* c8 ignore next */\n result += map[ch] ?? ch;\n } else {\n result += ch;\n }\n }\n return result;\n}\n\n// Parses a string with non-ASCII digits back to ASCII. The inverse of\n// convertDigits — used by parse() when an explicit `numberingSystem`\n// option is passed. Throws on unsupported systems.\nexport function convertDigitsToAscii(s: string, system: string): string {\n // Same dead-by-construction situation as applyParseNumbering's own\n // 'latn' guard: this function's only caller (applyParseNumbering)\n // already returns early on system === 'latn' before reaching this\n // call, so system is never 'latn' here in practice. Kept as a\n // defensive guard for any future direct caller.\n /* c8 ignore next */\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported.` });\n }\n const map = getDigitMap(system);\n // Build reverse map.\n const reverse: Record<string, string> = {};\n for (const k of Object.keys(map)) reverse[map[k]!] = k;\n let result = '';\n for (const ch of s) {\n result += reverse[ch] ?? ch;\n }\n return result;\n}\n\n// Augmented FormatOptions that includes the numberingSystem field.\n// Callers pass { numberingSystem: 'arab' } to format() to get Arabic-\n// Indic digits in the output.\nexport interface NumberingFormatOptions extends FormatOptions {\n numberingSystem?: string;\n}\n\n// Parse-side counterpart. Named parseNumberingSystem (not numberingSystem)\n// so a caller mixing format() and parse() options in the same config\n// object can set both independently — the two directions aren't always\n// symmetric (e.g. converting *to* native digits on output without\n// expecting native digits back on input, or vice versa).\nexport interface NumberingParseOptions extends FormatOptions {\n parseNumberingSystem?: string;\n}\n\n// Helper for the format path: takes the formatted ASCII output of\n// format() and converts digits if a numberingSystem was requested.\n// Kept here so format.ts doesn't need to know about numbering systems.\nexport function applyNumbering(s: string, options: NumberingFormatOptions): string {\n const system = options.numberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigits(s, system);\n}\n\n// Helper for the parse path: converts input digits to ASCII before\n// matching, when parseNumberingSystem is set. Distinct from the format\n// path's option name (numberingSystem vs parseNumberingSystem) so a\n// caller can be explicit about which direction they want transliterated.\nexport function applyParseNumbering(s: string, options: { parseNumberingSystem?: string }): string {\n // Both call sites of applyParseNumbering (parse.ts) already guard with\n // `if (options.parseNumberingSystem)` before calling it, so\n // options.parseNumberingSystem is always truthy here — the ?? 'latn'\n // fallback, and the subsequent 'latn' early return, are dead by\n // construction, not just untested. Kept as a defensive default rather\n // than assuming every future caller replicates the guard.\n /* c8 ignore next 2 */\n const system = options.parseNumberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigitsToAscii(s, system);\n}\n\n// Suppress unused-import warning. DEFAULT_LOCALE is imported for the\n// type augmentation pattern above; if the project ever exports a\n// typed-locale interface, it'll be the source of truth for the default.\nvoid DEFAULT_LOCALE;"],"mappings":"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","names":["SORTED_TOKEN_STRINGS","TOKENS","tok","a","b","tokenize","format","pieces","i","ch","appendLiteral","j","literal","closed","FormatSyntaxError","match","runChar","end","UnknownTokenError","value","last","SUPPORTED_NUMBERING_SYSTEMS","digitMapCache","getDigitMap","system","map","i","fmt","convertDigits","s","InvalidLocaleError","result","ch","convertDigitsToAscii","reverse","k","applyNumbering","options","applyParseNumbering"]}
@@ -0,0 +1,2 @@
1
+ import{n as p}from"./chunk-EHLRZULM.js";function x(n){let e=n.year,o=n.month,t=n.day,a=o<=2?e-1:e,m=Math.floor((a>=0?a:a-399)/400),b=a-m*400,y=o>2?o-3:o+9,w=Math.floor((153*y+2)/5)+t-1,d=b*365+Math.floor(b/4)-Math.floor(b/100)+w;return(m*146097+d-719468)*864e5+(n.hour??0)*36e5+(n.minute??0)*6e4+(n.second??0)*1e3+(n.millisecond??0)}function E(n){return p(n)}function u(n,e){let o=E(n),t=E(e),a=x(o),m=x(t);return a<m?-1:a>m?1:0}function V(n,e){return u(n,e)===0}function A(n,e){return u(n,e)<0}function U(n,e){return u(n,e)>0}function C(n){if(!Array.isArray(n)||n.length===0)throw new Error("temporal-fmt: min() requires a non-empty array of values.");let e=n[0];for(let o=1;o<n.length;o++)u(n[o],e)<0&&(e=n[o]);return e}function q(n){if(!Array.isArray(n)||n.length===0)throw new Error("temporal-fmt: max() requires a non-empty array of values.");let e=n[0];for(let o=1;o<n.length;o++)u(n[o],e)>0&&(e=n[o]);return e}function N(n,e,o){return u(n,e)<0?e:u(n,o)>0?o:n}function j(n,e,o){return u(n,e)>=0&&u(n,o)<=0}function r(n){let e=p(n);return{year:e.year,month:e.month,day:e.day}}function Y(){let n=new Date;return{year:n.getFullYear(),month:n.getMonth()+1,day:n.getDate()}}function k(n,e){return n.year===e.year&&n.month===e.month&&n.day===e.day}function H(n,e){return k(r(n),r(e))}function I(n){return k(r(n),Y())}function R(){let n=new Date;return n.setDate(n.getDate()+1),{year:n.getFullYear(),month:n.getMonth()+1,day:n.getDate()}}function F(){let n=new Date;return n.setDate(n.getDate()-1),{year:n.getFullYear(),month:n.getMonth()+1,day:n.getDate()}}function Q(n){return k(r(n),R())}function Z(n){return k(r(n),F())}function W(n,e,o,t,a,m){let y=(c,i,l)=>{let f=i<=2?c-1:c,D=(f>=0?f:f-399)/400|0,s=f-D*400,S=i>2?i-3:i+9,g=Math.floor(Math.floor(153*S+2)/5+l-1),T=s*365+Math.floor(s/4)-Math.floor(s/100)+g;return D*146097+T-719468},w=y(n,e,o),d=y(t,a,m);if(Math.abs(w-d)>=7)return!1;let h=(c,i,l)=>{let f=y(c,i,l),s=new Date(Date.UTC(c,i-1,l)).getUTCDay();return s===0?7:s},M=w-(h(n,e,o)-1),_=d-(h(t,a,m)-1);return M===_}function $(n,e){let o=r(n),t=r(e);return W(o.year,o.month,o.day,t.year,t.month,t.day)}function z(n,e){let o=r(n),t=r(e);return o.year===t.year&&o.month===t.month}function G(n,e){let o=r(n),t=r(e);return o.year===t.year&&Math.ceil(o.month/3)===Math.ceil(t.month/3)}function J(n,e){return r(n).year===r(e).year}function O(n){if(typeof n!="object"||n===null)throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(n)}.`);let e=n;if(typeof e.dayOfWeek!="number")throw new Error("temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).");return e.dayOfWeek===6||e.dayOfWeek===7}function K(n){return!O(n)}export{u as a,V as b,A as c,U as d,C as e,q as f,N as g,j as h,H as i,I as j,Q as k,Z as l,$ as m,z as n,G as o,J as p,O as q,K as r};
2
+ //# sourceMappingURL=chunk-UJR6DHXY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/comparison.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"],"mappings":"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","names":["toComparableMs","view","y","m","d","y2","era","yoe","m2","doy","doe","asDateTimeFieldView","value","asDateFieldView","compare","a","b","av","bv","am","bm","isEqual","isBefore","isAfter","min","values","best","i","max","clamp","lo","hi","isBetween","readYearMonthDay","v","todayYearMonthDay","now","dateEqualYearMonthDay","isSameDay","reference","isToday","tomorrowYearMonthDay","yesterdayYearMonthDay","isTomorrow","isYesterday","sameWeek","yearA","monthA","dayA","yearB","monthB","dayB","daysSince","weekdayOf","days","jsDow","wa","wb","isSameWeek","isSameMonth","isSameQuarter","isSameYear","isWeekend","isWeekday"]}
@@ -0,0 +1,21 @@
1
+ import{a as D,n as O}from"./chunk-EHLRZULM.js";function E(n){return O(n)}function p(n){if(typeof n.dayOfWeek!="number")return;/* c8 ignore start @preserve -- unreachable: every call site (add()'s
2
+ 'years'/'months' cases, shiftDays()) passes a `result` whose
3
+ year/month/day were just explicitly set, and the value originally
4
+ came from asDateTime()/asDateFieldView(), which throws unless
5
+ year/month/day are all present as numbers. So by the time this guard
6
+ runs, all three are guaranteed valid. Kept in case a future caller
7
+ constructs a DateTimeFieldView bypassing that validation. */if(typeof n.year!="number"||typeof n.month!="number"||typeof n.day!="number")return;/* c8 ignore stop @preserve */let e=new Date(Date.UTC(n.year,n.month-1,n.day)).getUTCDay();n.dayOfWeek=e===0?7:e}function i(n,e,t){let o={...E(n)};switch(t){case"years":{/* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:
8
+ asDateTime()/asDateFieldView() (called at the top of add()) throws
9
+ unless year is present as a number, so result.year is never
10
+ undefined here. Kept as a guard against a future change that
11
+ relaxes that validation. */o.year=(o.year??0)+e;/* c8 ignore stop @preserve */let s=D({year:o.year,month:o.month});o.day>s&&(o.day=s),p(o);break}case"months":{/* c8 ignore start @preserve -- both `??` fallbacks are unreachable
12
+ for the same reason as the 'years' case above: asDateFieldView()
13
+ guarantees year and month are present numbers before add() ever
14
+ reaches here. */let s=(o.year??0)*12+(o.month??1)-1+e;/* c8 ignore stop @preserve */o.year=Math.floor(s/12),o.month=s%12+1,o.month<1&&(o.month+=12,o.year-=1);/* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never
15
+ exceed 12 for any integer total in JS (its range is [-10, 12] since
16
+ JS's % can return a negative remainder but never one with
17
+ magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch
18
+ above is reachable; this was written symmetrically with it but the
19
+ overflow direction it guards against can't occur. Kept rather than
20
+ removed since it costs nothing and documents the intent. */o.month>12&&(o.month-=12,o.year+=1);/* c8 ignore stop @preserve */let a=D({year:o.year,month:o.month});o.day>a&&(o.day=a),p(o);break}case"weeks":o=S(o,e*7);break;case"days":o=S(o,e);break;case"hours":o=b(o,e*36e5);break;case"minutes":o=b(o,e*6e4);break;case"seconds":o=b(o,e*1e3);break;case"milliseconds":o=b(o,e);break}return o}function S(n,e){let t=n.year,r=n.month,o=n.day,s=r<=2?t-1:t,a=Math.floor((s>=0?s:s-399)/400),u=s-a*400,c=r>2?r-3:r+9,M=Math.floor((153*c+2)/5)+o-1,d=u*365+Math.floor(u/4)-Math.floor(u/100)+M,f=a*146097+d-719468+e+719468,m=Math.floor((f>=0?f:f-146096)/146097),_=f-m*146097,w=Math.floor((_-Math.floor(_/1460)+Math.floor(_/36524)-Math.floor(_/146096))/365),x=w+m*400,P=_-(365*w+Math.floor(w/4)-Math.floor(w/100)),k=Math.floor((5*P+2)/153),V=P-Math.floor((153*k+2)/5)+1,T=k<10?k+3:k-9,I=T<=2?x+1:x,U={...n,year:I,month:T,day:V};return p(U),U}function b(n,e){let a=(n.hour??0)*36e5+(n.minute??0)*6e4+(n.second??0)*1e3+(n.millisecond??0)+e,u=Math.floor(a/864e5),c=a%864e5;c<0&&(c+=864e5);let M=Math.floor(c/36e5),d=Math.floor(c%36e5/6e4),h=Math.floor(c%6e4/1e3),f=c%1e3,m={...n,hour:M,minute:d,second:h,millisecond:f};return u!==0?S(m,u):m}function y(n,e,t){return i(n,-e,t)}var C=(n,e)=>i(n,e,"years"),H=(n,e)=>i(n,e,"months"),A=(n,e)=>i(n,e,"weeks"),Y=(n,e)=>i(n,e,"days"),W=(n,e)=>i(n,e,"hours"),g=(n,e)=>i(n,e,"minutes"),j=(n,e)=>i(n,e,"seconds"),z=(n,e)=>i(n,e,"milliseconds"),q=(n,e)=>y(n,e,"years"),B=(n,e)=>y(n,e,"months"),G=(n,e)=>y(n,e,"weeks"),J=(n,e)=>y(n,e,"days"),K=(n,e)=>y(n,e,"hours"),L=(n,e)=>y(n,e,"minutes"),Q=(n,e)=>y(n,e,"seconds"),X=(n,e)=>y(n,e,"milliseconds");function l(n,e,t){let r=E(n),o=E(e);if(t==="years")return o.year-r.year;if(t==="months")return o.year*12+o.month-1-(r.year*12+r.month-1);if(t==="weeks"||t==="days"){let h=R(r),m=R(o)-h;return t==="weeks"?Math.trunc(m/7):m}let s=36e5,a=6e4,u=1e3,c=F(r),d=F(o)-c;switch(t){case"hours":return Math.trunc(d/s);case"minutes":return Math.trunc(d/a);case"seconds":return Math.trunc(d/u);case"milliseconds":return d}}function R(n){let e=n.year,t=n.month,r=n.day,o=t<=2?e-1:e,s=Math.floor((o>=0?o:o-399)/400),a=o-s*400,u=t>2?t-3:t+9,c=Math.floor((153*u+2)/5)+r-1,M=a*365+Math.floor(a/4)-Math.floor(a/100)+c;return s*146097+M-719468}function F(n){return R(n)*864e5+(n.hour??0)*36e5+(n.minute??0)*6e4+(n.second??0)*1e3+(n.millisecond??0)}var Z=(n,e)=>l(n,e,"years"),$=(n,e)=>l(n,e,"months"),v=(n,e)=>l(n,e,"weeks"),nn=(n,e)=>l(n,e,"days"),en=(n,e)=>l(n,e,"hours"),on=(n,e)=>l(n,e,"minutes"),tn=(n,e)=>l(n,e,"seconds"),sn=(n,e)=>l(n,e,"milliseconds");export{i as a,y as b,C as c,H as d,A as e,Y as f,W as g,g as h,j as i,z as j,q as k,B as l,G as m,J as n,K as o,L as p,Q as q,X as r,l as s,Z as t,$ as u,v,nn as w,en as x,on as y,tn as z,sn as A};
21
+ //# sourceMappingURL=chunk-VQXUMFB2.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/arithmetic.ts"],"sourcesContent":["// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.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 totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.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 return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/arithmetic.ts"],"sourcesContent":["// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.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 totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.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 return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');"],"mappings":"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","names":["asDateTime","value","asDateFieldView","recomputeDayOfWeek","view","jsDow","add","amount","unit","result","maxDay","daysInMonth","total","shiftDays","shiftTime","v","days","y","m","d","y2","era","yoe","m2","doy","doe","z","era2","doe2","yoe2","y2out","doy2","mp","d2","m2out","yOut","msDelta","totalMs","dayOverflow","withinDay","hour","minute","second","millisecond","subtract","addYears","n","addMonths","addWeeks","addDays","addHours","addMinutes","addSeconds","addMilliseconds","subtractYears","subtractMonths","subtractWeeks","subtractDays","subtractHours","subtractMinutes","subtractSeconds","subtractMilliseconds","difference","a","b","av","bv","aDays","toDayCount","MS_PER_HOUR","MS_PER_MINUTE","MS_PER_SECOND","aMs","toTotalMs","diffMs","differenceInYears","differenceInMonths","differenceInWeeks","differenceInDays","differenceInHours","differenceInMinutes","differenceInSeconds","differenceInMilliseconds"]}
@@ -0,0 +1,2 @@
1
+ import{k as o,o as u,t as d}from"./chunk-5U5WJ465.js";var a=new Map,h=256,L=256;function g(t){try{return new Intl.Locale(t.replace(/_/g,"-")).toString().toLowerCase()}catch{return t}}function w(t,r){if(typeof t!="string"||t.length===0)throw new o({actual:String(t),reason:"locale string must be non-empty"});if(t.length>h)throw new o({actual:String(t.length),reason:`locale tag must be at most ${h} characters`});u(t,r),f(r,t);let e=g(t);a.set(e,{...r})}function f(t,r){let e=(n,i,c)=>{if(n!==void 0){if(!Array.isArray(n))throw new o({actual:String(n),reason:`locale "${r}": "${i}" must be an array`});if(n.length!==c)throw new o({actual:String(n.length),reason:`locale "${r}": "${i}" must have ${c} entries (got ${n.length})`});for(let s=0;s<n.length;s++){if(typeof n[s]!="string"||n[s].length===0)throw new o({actual:String(n[s]),reason:`locale "${r}": "${i}[${s}]" must be a non-empty string`});if(n[s].length>L)throw new o({actual:String(n[s].length),reason:`locale "${r}": "${i}[${s}]" must be at most ${L} characters`})}}};e(t.quartersLong,"quartersLong",4),e(t.quartersShort,"quartersShort",4),e(t.erasLong,"erasLong",2),e(t.erasShort,"erasShort",2),e(t.ordinals,"ordinals",4)}function b(t){let r=g(t),e=a.get(r);if(e)return e;try{return{...d(t)}}catch{return}}function p(t){let r=g(t);return a.has(r)}export{w as a,b,p as c};
2
+ //# sourceMappingURL=chunk-XH46JNPK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/localeRegistry.ts"],"sourcesContent":["// Locale registration and lookup. Extends the\n// existing registerLocaleVocab() surface in localeVocab.ts with\n// registerLocale() / getLocale() / hasLocale() and a richer LocaleVocab\n// that covers quarters, eras, ordinals, duration units, and relative-\n// time language.\n//\n// The existing localeVocab.ts stays as the implementation for months/\n// weekdays/day-periods (those are what tokens.ts's MMMM/MMM/EEEE/EEE/a\n// tokens read). This module wraps it and adds the extended fields.\n\nimport { registerLocaleVocab, getLocaleVocab, type LocaleVocab as BaseLocaleVocab } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface ExtendedLocaleVocab extends BaseLocaleVocab {\n // Quarters: Q1..Q4 long form (\"First quarter\", etc.). Default falls\n // back to \"Q1\"..\"Q4\" if not registered.\n quartersLong?: string[];\n quartersShort?: string[];\n // Eras: [\"BC\", \"AD\"] for gregory, etc. Default [\"BC\", \"AD\"].\n erasLong?: string[];\n erasShort?: string[];\n // Ordinal suffix rules: [\"st\", \"nd\", \"rd\", \"th\"] for English. Default\n // empty (the existing 'do' token has English-only ordinal logic baked\n // into tokens.ts; this field lets a custom locale override it).\n ordinals?: string[];\n // Duration unit names: long singular/plural for each unit. Used by\n // formatDuration's locale-aware path when Intl.NumberFormat isn't\n // desired (e.g. for a made-up locale Intl doesn't know about).\n durationUnits?: {\n years: [string, string];\n months: [string, string];\n weeks: [string, string];\n days: [string, string];\n hours: [string, string];\n minutes: [string, string];\n seconds: [string, string];\n milliseconds: [string, string];\n };\n // Relative-time language: words for \"ago\" / \"in\" / \"now\". Used by\n // formatRelative()'s locale-aware path when Intl.RelativeTimeFormat\n // isn't available or doesn't cover a registered locale.\n relativeTime?: {\n past: string;\n future: string;\n now: string;\n };\n}\n\n// Bounded indirectly: every entry here is added by registerLocale(), which\n// first passes registerLocaleVocab() — and THAT registry caps new locales\n// at 500 (MAX_CUSTOM_VOCABS, RangeError past it). So extendedVocabs can\n// never exceed 500 keys; no separate cap is needed (and an unreachable one\n// would only add dead branches).\nconst extendedVocabs = new Map<string, ExtendedLocaleVocab>();\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_STRING_LENGTH = 256;\n\nfunction canonicalKey(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n return locale;\n }\n}\n\nexport function registerLocale(locale: string, vocab: ExtendedLocaleVocab): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new InvalidLocaleError({ actual: String(locale), reason: 'locale string must be non-empty' });\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new InvalidLocaleError({ actual: String(locale.length), reason: `locale tag must be at most ${MAX_LOCALE_TAG_LENGTH} characters` });\n }\n // Validate the base vocab fields (months/weekdays/day-periods) via\n // the existing registerLocaleVocab — it has the strict-shape checks.\n registerLocaleVocab(locale, vocab);\n // Extended fields are optional; only validate the ones that are present.\n validateExtended(vocab, locale);\n const key = canonicalKey(locale);\n extendedVocabs.set(key, { ...vocab });\n}\n\nfunction validateExtended(vocab: ExtendedLocaleVocab, locale: string): void {\n const checkArray = (val: unknown, key: string, length: number) => {\n if (val === undefined) return;\n if (!Array.isArray(val)) {\n throw new InvalidLocaleError({ actual: String(val), reason: `locale \"${locale}\": \"${key}\" must be an array` });\n }\n if (val.length !== length) {\n throw new InvalidLocaleError({ actual: String(val.length), reason: `locale \"${locale}\": \"${key}\" must have ${length} entries (got ${val.length})` });\n }\n for (let i = 0; i < val.length; i++) {\n if (typeof val[i] !== 'string' || val[i]!.length === 0) {\n throw new InvalidLocaleError({ actual: String(val[i]), reason: `locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string` });\n }\n if (val[i]!.length > MAX_VOCAB_STRING_LENGTH) {\n throw new InvalidLocaleError({ actual: String(val[i]!.length), reason: `locale \"${locale}\": \"${key}[${i}]\" must be at most ${MAX_VOCAB_STRING_LENGTH} characters` });\n }\n }\n };\n checkArray(vocab.quartersLong, 'quartersLong', 4);\n checkArray(vocab.quartersShort, 'quartersShort', 4);\n checkArray(vocab.erasLong, 'erasLong', 2);\n checkArray(vocab.erasShort, 'erasShort', 2);\n checkArray(vocab.ordinals, 'ordinals', 4);\n}\n\nexport function getLocale(locale: string): ExtendedLocaleVocab | undefined {\n // Returns the extended vocab for a locale, OR undefined (falls back\n // to Intl). Combines the base vocab (from localeVocab.ts, which\n // includes Intl defaults) with any extended fields registered here.\n const key = canonicalKey(locale);\n const ext = extendedVocabs.get(key);\n if (ext) return ext;\n // Fall back to base vocab (Intl-derived) without extended fields.\n try {\n const base = getLocaleVocab(locale);\n return { ...base };\n } catch {\n return undefined;\n }\n}\n\nexport function hasLocale(locale: string): boolean {\n const key = canonicalKey(locale);\n return extendedVocabs.has(key);\n}\n\n// Re-export the base vocab types for callers that want them.\nexport type { LocaleVocab } from './localeVocab.js';\n"],"mappings":"sDAqDA,IAAMA,EAAiB,IAAI,IACrBC,EAAwB,IACxBC,EAA0B,IAEhC,SAASC,EAAaC,EAAwB,CAC5C,GAAI,CACF,OAAO,IAAI,KAAK,OAAOA,EAAO,QAAQ,KAAM,GAAG,CAAC,EAAE,SAAS,EAAE,YAAY,CAC3E,MAAQ,CACN,OAAOA,CACT,CACF,CAEO,SAASC,EAAeD,EAAgBE,EAAkC,CAC/E,GAAI,OAAOF,GAAW,UAAYA,EAAO,SAAW,EAClD,MAAM,IAAIG,EAAmB,CAAE,OAAQ,OAAOH,CAAM,EAAG,OAAQ,iCAAkC,CAAC,EAEpG,GAAIA,EAAO,OAASH,EAClB,MAAM,IAAIM,EAAmB,CAAE,OAAQ,OAAOH,EAAO,MAAM,EAAG,OAAQ,8BAA8BH,CAAqB,aAAc,CAAC,EAI1IO,EAAoBJ,EAAQE,CAAK,EAEjCG,EAAiBH,EAAOF,CAAM,EAC9B,IAAMM,EAAMP,EAAaC,CAAM,EAC/BJ,EAAe,IAAIU,EAAK,CAAE,GAAGJ,CAAM,CAAC,CACtC,CAEA,SAASG,EAAiBH,EAA4BF,EAAsB,CAC1E,IAAMO,EAAa,CAACC,EAAcF,EAAaG,IAAmB,CAChE,GAAID,IAAQ,OACZ,IAAI,CAAC,MAAM,QAAQA,CAAG,EACpB,MAAM,IAAIL,EAAmB,CAAE,OAAQ,OAAOK,CAAG,EAAG,OAAQ,WAAWR,CAAM,OAAOM,CAAG,oBAAqB,CAAC,EAE/G,GAAIE,EAAI,SAAWC,EACjB,MAAM,IAAIN,EAAmB,CAAE,OAAQ,OAAOK,EAAI,MAAM,EAAG,OAAQ,WAAWR,CAAM,OAAOM,CAAG,eAAeG,CAAM,iBAAiBD,EAAI,MAAM,GAAI,CAAC,EAErJ,QAASE,EAAI,EAAGA,EAAIF,EAAI,OAAQE,IAAK,CACnC,GAAI,OAAOF,EAAIE,CAAC,GAAM,UAAYF,EAAIE,CAAC,EAAG,SAAW,EACnD,MAAM,IAAIP,EAAmB,CAAE,OAAQ,OAAOK,EAAIE,CAAC,CAAC,EAAG,OAAQ,WAAWV,CAAM,OAAOM,CAAG,IAAII,CAAC,+BAAgC,CAAC,EAElI,GAAIF,EAAIE,CAAC,EAAG,OAASZ,EACnB,MAAM,IAAIK,EAAmB,CAAE,OAAQ,OAAOK,EAAIE,CAAC,EAAG,MAAM,EAAG,OAAQ,WAAWV,CAAM,OAAOM,CAAG,IAAII,CAAC,sBAAsBZ,CAAuB,aAAc,CAAC,CAEvK,EACF,EACAS,EAAWL,EAAM,aAAc,eAAgB,CAAC,EAChDK,EAAWL,EAAM,cAAe,gBAAiB,CAAC,EAClDK,EAAWL,EAAM,SAAU,WAAY,CAAC,EACxCK,EAAWL,EAAM,UAAW,YAAa,CAAC,EAC1CK,EAAWL,EAAM,SAAU,WAAY,CAAC,CAC1C,CAEO,SAASS,EAAUX,EAAiD,CAIzE,IAAMM,EAAMP,EAAaC,CAAM,EACzBY,EAAMhB,EAAe,IAAIU,CAAG,EAClC,GAAIM,EAAK,OAAOA,EAEhB,GAAI,CAEF,MAAO,CAAE,GADIC,EAAeb,CAAM,CACjB,CACnB,MAAQ,CACN,MACF,CACF,CAEO,SAASc,EAAUd,EAAyB,CACjD,IAAMM,EAAMP,EAAaC,CAAM,EAC/B,OAAOJ,EAAe,IAAIU,CAAG,CAC/B","names":["extendedVocabs","MAX_LOCALE_TAG_LENGTH","MAX_VOCAB_STRING_LENGTH","canonicalKey","locale","registerLocale","vocab","InvalidLocaleError","registerLocaleVocab","validateExtended","key","checkArray","val","length","i","getLocale","ext","getLocaleVocab","hasLocale"]}
@@ -0,0 +1,35 @@
1
+ import{a as ue,f as q}from"./chunk-R52YOKI3.js";import{a as F}from"./chunk-C5YESWFT.js";import{c as me}from"./chunk-3MZLTVP3.js";import{a as se,b as h,c as G,d as $,e as M,g as A,h as V,j as K,m as oe,n as ae,q as j,r as ie,t as v}from"./chunk-5U5WJ465.js";function Se(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function R(e,n=!1){let t=e.map(Se);return n?`(?:${t.map(ye).join("|")})`:`(?:${t.join("|")})`}function ye(e){return e.replace(/[a-zA-Z]/g,n=>`[${n.toLowerCase()}${n.toUpperCase()}]`)}var be="(?:UTC|[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\d{1,2})?(?:\\/[A-Za-z0-9_+-]+)*)",xe={X:"(?:Z|[+-]\\d{2}(?:\\d{2})?)",XX:"(?:Z|[+-]\\d{4})",XXX:"(?:Z|[+-]\\d{2}:\\d{2})",x:"[+-]\\d{2}(?:\\d{2})?",xx:"[+-]\\d{4}",xxx:"[+-]\\d{2}:\\d{2}"};function ke(){return be}var Z;function $e(){return Z||(Z=new Set(Intl.supportedValuesOf("timeZone")),Z.add("UTC")),Z}var Ee=/^[+-]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;function J(e){return Ee.test(e)||$e().has(e)}var Pe={yy:"\\d{2}",MM:"(?:0[1-9]|1[0-2])",M:"(?:1[0-2]|[1-9])",dd:"(?:0[1-9]|[12]\\d|3[01])",d:"(?:[12]\\d|3[01]|[1-9])",HH:"(?:[01]\\d|2[0-3])",H:"(?:1\\d|2[0-3]|[0-9])",hh:"(?:0[1-9]|1[0-2])",h:"(?:1[0-2]|[1-9])",mm:"(?:[0-5]\\d)",m:"(?:[1-5]\\d|[0-9])",ss:"(?:[0-5]\\d)",s:"(?:[1-5]\\d|[0-9])",SSSSSSSSS:"\\d{9}",SSSSSSSS:"\\d{8}",SSSSSSS:"\\d{7}",SSSSSS:"\\d{6}",SSSSS:"\\d{5}",SSSS:"\\d{4}",SSS:"\\d{3}",SS:"\\d{2}",S:"\\d",Q:"[1-4]"},Ne="Q[1-4]",Me=new Set(["do","ww","RRRR","D","DD","DDD","LLLL","LLL","cccc","ccc","GGGG","G","zzzz","z"]),ve="-?\\d{4}",Te="-?\\d{4,}",B=new Set(["yyyy","yy","MM","M","dd","d","HH","H","hh","h","mm","m","ss","s","SSSSSSSSS","SSSSSSSS","SSSSSSS","SSSSSS","SSSSS","SSSS","SSS","SS","S"]);function H(e,n,t){if(e==="yyyy")return t!==void 0&&B.has(t)?ve:Te;let r=Pe[e];if(r)return r;if(e==="QQQ")return Ne;if(Me.has(e))throw new G({token:e,message:`temporal-fmt: token "${e}" is format-only \u2014 it can't be parsed back into a value. Use a different token in the parse format string (e.g. "d" for "do", "MM" for "ww").`});let s=v(n);switch(e){case"MMMM":return R(s.monthLong);case"MMM":return R(s.monthShort);case"EEEE":return R(s.weekdayLong);case"EEE":return R(s.weekdayShort);case"a":return R(s.dayPeriod,!0);case"zzz":return ke();case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":return xe[e];default:throw new G({token:e,message:`temporal-fmt: unknown token "${e}"`})}}var de=new Set(["M","d","H","h","m","s"]),Oe={M:[{digits:1,min:1,max:9},{digits:2,min:10,max:12}],d:[{digits:1,min:1,max:9},{digits:2,min:10,max:31}],H:[{digits:1,min:0,max:9},{digits:2,min:10,max:23}],h:[{digits:1,min:1,max:9},{digits:2,min:10,max:12}],m:[{digits:1,min:0,max:9},{digits:2,min:10,max:59}],s:[{digits:1,min:0,max:9},{digits:2,min:10,max:59}]};function W(e,n){let t=new Map;function r(s,o){let i=`${s}:${o}`,u=t.get(i);if(u)return u;if(s===n.length){let a=o===e.length?[[]]:[];return t.set(i,a),a}let S=n[s],l=Oe[S];/* c8 ignore start @preserve -- defensive guard, not reachable through
2
+ the public API. enumerateValidSplits's only caller (parse.ts, both
3
+ call sites) passes run.tokens straight from
4
+ pattern.ambiguousRuns, which parsePattern.ts only ever populates
5
+ with tokens already checked against UNPADDED_NUMERIC_TOKENS — the
6
+ exact same key set as UNPADDED_NUMERIC_RANGES. There's no path
7
+ where a token reaches here without having already passed that
8
+ check. */if(!l)throw new Error(`temporal-fmt: internal error \u2014 "${S}" is not an unpadded numeric token`);/* c8 ignore stop @preserve */let m=[];for(let{digits:a,min:g,max:f}of l){if(o+a>e.length)continue;let p=e.slice(o,o+a);if(a===2&&p[0]==="0")continue;let y=Number(p);if(!(y<g||y>f)){for(let D of r(s+1,o+a))if(m.push([y,...D]),m.length===2)break;if(m.length===2)break}}return t.set(i,m),m}return r(0,0)}function De(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var ce=12;function Xe(e){return Math.ceil(Math.log2(Math.max(e,1)))}function ee(e){return e===void 0?!1:e.kind==="literal"?/^[0-9]/.test(e.value):B.has(e.value)}function ge(e,n){let t=[],r=[],s="",o=0,i=0,u={names:[],tokens:[]},S=l=>{if(u.tokens.length===1){let m=u.names[0],a=u.tokens[0];s+=`(?<${m}>${H(a,n)})`,ee(l)&&(i+=1)}else if(u.tokens.length>=2){let m=`r${o++}`,a=u.tokens.length;s+=`(?<${m}>\\d{${a},${a*2}})`,r.push({groupName:m,groupNames:u.names,tokens:u.tokens}),ee(l)&&(i+=Xe(a+1))}u={names:[],tokens:[]}};for(let[l,m]of e.entries()){if(m.kind==="literal"){S(m),s+=De(m.value);continue}if(de.has(m.value)){let p=`g${o++}`;t.push({name:p,token:m.value}),u.names.push(p),u.tokens.push(m.value);continue}S(m);let a=`g${o++}`;t.push({name:a,token:m.value});let g=e[l+1],f=g?.kind==="token"?g.value:void 0;m.value==="yyyy"&&f===void 0&&ee(g)?s+=`(?<${a}>${H(m.value,n,"M")})`:s+=`(?<${a}>${H(m.value,n,f)})`}if(S(void 0),i>ce)throw new h({reason:`format string has too many variable-width numeric tokens glued to digit-consuming neighbors (ambiguity score ${i} > ${ce}). This shape makes the regex engine backtrack exponentially on near-miss input. Add a non-digit separator between these tokens (e.g. "-" or " ") or use their padded forms (MM/dd/HH/mm/ss).`});return{regex:new RegExp(`^(?:${s})$`,"ud"),groups:t,ambiguousRuns:r}}var O=new Map,Ce=500;ae(()=>{O.clear()});function te(e,n){let t=JSON.stringify([j(n),e]),r=O.get(t);if(r)return r;if(O.size>=Ce){let s=O.keys().next().value;s!==void 0&&O.delete(s)}return r=ge(ue(e),n),O.set(t,r),r}var T=new Map,Ae=500;function Re(e){ie(e);let n=j(e);if(T.has(n))return T.get(n);if(T.size>=Ae){let i=T.keys().next().value;i!==void 0&&T.delete(i)}let t,r=n.split("-"),s=r.indexOf("u"),o=s===-1?-1:r.indexOf("ca",s+1);if(o!==-1&&o+1<r.length){let i=new Intl.DateTimeFormat(n).resolvedOptions().calendar;t=i==="gregory"?void 0:i}return T.set(n,t),t}function _e(e,n){if(e==="Z"){/* c8 ignore start @preserve -- unreachable: lowercase tokens' regex
9
+ (OFFSET_SHAPES in pattern.ts) has no "Z" alternative at all, so
10
+ raw === 'Z' can only ever be reached when token is one of the
11
+ uppercase variants (X/XX/XXX). A lowercase token can't even
12
+ capture "Z" as `raw` in the first place. */if(n==="x"||n==="xx"||n==="xxx")throw new Error(`temporal-fmt: offset token "${n}" doesn't accept "Z" \u2014 only the uppercase variants (X/XX/XXX) emit "Z" for UTC. Use "+00:00", "+0000", or "+00" depending on the variant's width.`);/* c8 ignore stop @preserve */return"+00:00"}let t=e[0];/* c8 ignore start @preserve -- unreachable: raw is a regex-captured
13
+ group from an offset token, and every OFFSET_SHAPES pattern
14
+ (pattern.ts) is anchored to either "Z" or a leading [+-]. raw's
15
+ first character can never be anything else by the time it reaches
16
+ this function. */if(t!=="+"&&t!=="-")throw new Error(`temporal-fmt: offset "${e}" for token "${n}" doesn't start with "+", "-", or "Z".`);/* c8 ignore stop @preserve */let r=e.slice(1),s,o;if(r.length===2){/* c8 ignore start @preserve -- unreachable: each offset token's own
17
+ regex shape in OFFSET_SHAPES (pattern.ts) already gates which
18
+ body shapes it can capture. Only X and x ever match a 2-digit
19
+ body — XX/xx/XXX/xxx's regexes can't produce one — so this
20
+ mismatch can never actually fire through parse(). */if(n!=="X"&&n!=="x")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it requires minutes, but "${e}" has none.`);/* c8 ignore stop @preserve */s=r,o="00"}else if(r.length===4){/* c8 ignore start @preserve -- unreachable, same reason as the
21
+ 2-digit case above: XXX/xxx's regex requires a colon, so it can
22
+ never capture a 4-digit no-colon body in the first place. */if(n==="XXX"||n==="xxx")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it requires a colon between hours and minutes (e.g. "${t}${r.slice(0,2)}:${r.slice(2)}").`);/* c8 ignore stop @preserve */s=r.slice(0,2),o=r.slice(2,4)}else if(r.length===5&&r[2]===":"){/* c8 ignore start @preserve -- unreachable, same reason again: only
23
+ XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never
24
+ capture one. */if(n!=="XXX"&&n!=="xxx")throw new Error(`temporal-fmt: offset token "${n}" can't match "${e}" \u2014 it doesn't use a colon (use "${t}${r.slice(0,2)}${r.slice(3)}" instead).`);/* c8 ignore stop @preserve */s=r.slice(0,2),o=r.slice(3,5);/* c8 ignore start @preserve -- unreachable: every offset token's
25
+ regex only ever produces a body of length 2, length 4, or length 5
26
+ with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no
27
+ shape falls outside those three cases, so this else arm can't be
28
+ taken through parse(). Kept as an exhaustiveness fallback so
29
+ hoursStr/minutesStr are assigned on every path TypeScript can see. */}else throw new Error(`temporal-fmt: offset "${e}" doesn't match the shape token "${n}" accepts.`);/* c8 ignore stop @preserve */let i=Number(s),u=Number(o);if(i>14)throw new A({actual:e,message:`temporal-fmt: offset hours ${i} in "${e}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`});if(u>59)throw new A({actual:e,message:`temporal-fmt: offset minutes ${u} in "${e}" out of range (max 59).`});if(t==="+"&&i===14&&u!==0)throw new A({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported UTC offset of +14:00.`});if(t==="-"&&i===12&&u!==0)throw new A({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported negative UTC offset of -12:00.`});return`${t}${s}:${o}`}function d(e,n,t){e[n]=t}function ze(e,n,t,r,s){let o=v(r);switch(n){case"yyyy":d(e,"year",Number(t));break;case"yy":d(e,"twoDigitYear",Number(t));break;case"MM":case"M":d(e,"month",Number(t));break;case"MMMM":d(e,"month",o.monthLong.indexOf(t)+1);break;case"MMM":d(e,"month",o.monthShort.indexOf(t)+1);break;case"dd":case"d":d(e,"day",Number(t));break;case"EEEE":d(e,"weekdayRaw",t),d(e,"weekdayExpected",o.weekdayLong.indexOf(t)+1);break;case"EEE":d(e,"weekdayRaw",t),d(e,"weekdayExpected",o.weekdayShort.indexOf(t)+1);break;case"HH":case"H":d(e,"hour",Number(t));break;case"hh":case"h":d(e,"hour12",Number(t));break;case"mm":case"m":d(e,"minute",Number(t));break;case"ss":case"s":d(e,"second",Number(t));break;case"S":case"SS":case"SSS":case"SSSS":case"SSSSS":case"SSSSSS":case"SSSSSSS":case"SSSSSSSS":case"SSSSSSSSS":{let i=Number(t.padEnd(9,"0"));d(e,"millisecond",Math.floor(i/1e6)),d(e,"microsecond",Math.floor(i/1e3)%1e3),d(e,"nanosecond",i%1e3);break}case"a":{let i=o.dayPeriod.findIndex(u=>u.toLowerCase()===t.toLowerCase());/* c8 ignore start @preserve -- unreachable: the 'a' token's regex
30
+ fragment (pattern.ts's alternation() over vocab.dayPeriod) can
31
+ only ever capture a case-insensitive match of one of
32
+ vocab.dayPeriod's own entries. Both the regex and this lookup
33
+ derive their vocab from the same `locale` via getLocaleVocab(),
34
+ so periodIndex can't come back negative through parse(). */if(i<0)throw new Error(`temporal-fmt: unknown day period "${t}" for locale "${r}".`);/* c8 ignore stop @preserve */d(e,"dayPeriodRaw",t),d(e,"isPM",i===1);break}case"zzz":d(e,"timeZoneId",t);break;case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":d(e,"offsetString",_e(t,n));break;case"Q":d(e,"quarter",Number(t));break;case"QQQ":d(e,"quarter",Number(t.slice(1)));break}}function fe(e,n){let t=n.indexOf("d");if(t!==-1){let r=e.filter(s=>s[t]<=12);if(r.length>0)return r[0]}return e[0]}function Ie(e){if(e.year!==void 0&&e.twoDigitYear!==void 0)throw new h({message:'temporal-fmt: format string mixes "yyyy" and "yy" year representations.'});if(e.year!==void 0)return e.year;if(e.twoDigitYear!==void 0)return e.twoDigitYear<=68?2e3+e.twoDigitYear:1900+e.twoDigitYear}function Le(e,n,t){if(e.hour!==void 0&&e.hour12!==void 0)throw new h({format:n,message:`temporal-fmt: format string "${n}" mixes a 24-hour token ("HH"/"H") with a 12-hour token ("hh"/"h").`});if(e.hour!==void 0){if(e.dayPeriodRaw!==void 0){let r=v(t),s=e.hour<12?r.dayPeriod[0]:r.dayPeriod[1];if(e.dayPeriodRaw.toLowerCase()!==s?.toLowerCase())throw new h({format:n,message:`temporal-fmt: format string "${n}" contains a day period that contradicts the 24-hour value.`})}return e.hour}if(e.hour12!==void 0){if(e.isPM===void 0)throw new h({format:n,message:`temporal-fmt: format string "${n}" uses a 12-hour token ("hh"/"h") without an "a" token, so parse() can't tell AM from PM.`});return e.hour12%12+(e.isPM?12:0)}}function re(e,n,t={}){if(e.length>1e3)throw new h({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(n.length>1e5)throw new h({input:n,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${n.length}).`});t.parseNumberingSystem&&(n=q(n,t));let r=t.locale??F,s=Re(r),o=te(e,r),i=o.regex.exec(n);if(!i)throw new $({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(o.groups.length===0)throw new $({format:e,reason:`format string "${e}" has no tokens \u2014 nothing to parse into a value.`,message:`temporal-fmt: format string "${e}" has no tokens \u2014 nothing to parse into a value.`});for(let{name:c,token:w}of o.groups)if(w==="zzz"&&!J(i.groups[c]))throw new V({input:n,format:e,actual:i.groups[c],reason:"not a recognized IANA time zone identifier"});let u=new Map;for(let c of o.ambiguousRuns){let w=i.groups[c.groupName],b=W(w,c.tokens);if(b.length===0)throw new $({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(b.length>1){if(!t.lenient)throw new K({input:n,format:e,message:`temporal-fmt: "${w}" in format string "${e}" is ambiguous \u2014 ${b.length} different ways to read tokens "${c.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(b[0])} vs ${JSON.stringify(b[1])}). parse() won't guess; add a separator between these tokens, or use their padded form (e.g. "MM" instead of "M") so each one has a fixed width. Pass { lenient: true } to opt into a documented heuristic that picks one.`});let N=fe(b,c.tokens);c.groupNames.forEach((Y,we)=>u.set(Y,String(N[we])))}else c.groupNames.forEach((N,Y)=>u.set(N,String(b[0][Y])))}let S={};for(let{name:c,token:w}of o.groups){let b=u.get(c)??i.groups[c];ze(S,w,b,r,e)}let l=Ie(S),m=Le(S,e,r),{month:a,day:g,minute:f,second:p,millisecond:y,microsecond:D,nanosecond:le,timeZoneId:X,offsetString:x,weekdayExpected:U,weekdayRaw:he,quarter:Q}=S,pe=l!==void 0||a!==void 0||g!==void 0,E=l!==void 0&&a!==void 0&&g!==void 0;if(pe&&!E)throw new M({format:e,message:`temporal-fmt: format string "${e}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`});let z=m!==void 0||f!==void 0||p!==void 0||y!==void 0;if(X!==void 0&&!(E&&z))throw new h({format:e,message:`temporal-fmt: format string "${e}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`});if(x!==void 0&&!(E&&z))throw new h({format:e,message:`temporal-fmt: format string "${e}" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time to build a ZonedDateTime.`});if(U!==void 0&&!E)throw new h({format:e,message:`temporal-fmt: format string "${e}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`});if(!E&&!z)throw new h({format:e,message:`temporal-fmt: format string "${e}" has no date or time tokens to parse.`});let C=me(),P={hour:m??0,minute:f??0,second:p??0,millisecond:y??0,microsecond:D??0,nanosecond:le??0},I=s?{calendar:s}:{},L={overflow:"reject"},k;try{if(X!==void 0){let c={overflow:"reject",offset:"prefer"};if(k=C.ZonedDateTime.from({year:l,month:a,day:g,...P,...I,timeZone:X,...x!==void 0?{offset:x}:{}},c),x!==void 0){let w=k;if(w.hour!==P.hour||w.minute!==P.minute||w.second!==P.second)throw new M({input:n,format:e,message:`"${X}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`});let N=w.offset;if(N!==x)throw new $({input:n,format:e,message:`has both a "zzz" zone (${X}) and an offset token (${x}), but the zone's actual offset at this date/time is ${N}, not ${x}.`})}}else x!==void 0?k=C.ZonedDateTime.from({year:l,month:a,day:g,...P,...I,timeZone:x},L):E&&z?k=C.PlainDateTime.from({year:l,month:a,day:g,...P,...I},L):E?k=C.PlainDate.from({year:l,month:a,day:g,...I},L):k=C.PlainTime.from(P,L)}catch(c){throw new M({input:n,format:e,message:`temporal-fmt: "${n}" doesn't describe a valid date/time for format "${e}": ${c.message}`})}if(U!==void 0){let c=k.dayOfWeek;if(c!==U){let w=v(r);throw new M({input:n,format:e,message:`temporal-fmt: "${he}" doesn't match the actual weekday (${w.weekdayLong[c-1]}) for the parsed date.`})}}if(Q!==void 0&&a!==void 0){let c=Math.ceil(a/3);if(Q!==c)throw new M({format:e,message:`temporal-fmt: format string "${e}" contains a quarter token (Q/QQQ) whose value (Q${Q}) disagrees with the parsed month's actual quarter \u2014 month ${a} is in Q${c}.`})}return k}function Fe(e,n,t={}){try{return{ok:!0,value:re(e,n,t)}}catch(r){/* c8 ignore start @preserve */return r instanceof se?{ok:!1,error:r}:{ok:!1,error:oe(r,{input:n,format:e})}}/* c8 ignore stop @preserve */}function Ze(e,n,t={}){try{return re(e,n,t)}catch{return}}function He(e,n,t={}){if(e.length>1e3)throw new h({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(n.length>1e5)throw new h({input:n,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${n.length}).`});t.parseNumberingSystem&&(n=q(n,t));let r=t.locale??F,s=te(e,r),o=s.regex.exec(n);if(!o)throw new $({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(s.groups.length===0)throw new $({format:e,reason:`format string "${e}" has no tokens \u2014 nothing to parse into a value.`,message:`temporal-fmt: format string "${e}" has no tokens \u2014 nothing to parse into a value.`});for(let{name:a,token:g}of s.groups)if(g==="zzz"&&!J(o.groups[a]))throw new V({input:n,format:e,actual:o.groups[a],reason:"not a recognized IANA time zone identifier"});let i=new Map;for(let a of s.ambiguousRuns){let g=o.groups[a.groupName],f=W(g,a.tokens);if(f.length===0)throw new $({input:n,format:e,reason:"no valid pattern matches the format string and input shape",message:"temporal-fmt: no valid pattern matches the format string and input shape"});if(f.length>1){if(!t.lenient)throw new K({input:n,format:e,message:`temporal-fmt: "${g}" in format string "${e}" is ambiguous \u2014 ${f.length} different ways to read tokens "${a.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(f[0])} vs ${JSON.stringify(f[1])}). parse() won't guess; add a separator between these tokens, or use their padded form (e.g. "MM" instead of "M") so each one has a fixed width. Pass { lenient: true } to opt into a documented heuristic that picks one.`});let p=fe(f,a.tokens);a.groupNames.forEach((y,D)=>i.set(y,String(p[D])))}else a.groupNames.forEach((p,y)=>i.set(p,String(f[0][y])))}let u=[],l=o.indices?.groups,m=0;for(let{name:a,token:g}of s.groups){let f=i.get(a)??o.groups[a],p=!i.has(a)&&l?.[a],y=p?p[0]:(o.index??0)+m;u.push({token:g,raw:f,position:y}),m+=f.length}return u}function rn(e,n={}){if(e.length>1e3)throw new h({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});let t=n.locale??F,r=te(e,t);return{formatStr:e,pattern:r,parse(s,o={}){return re(e,s,{locale:t,...o})},safeParse(s,o={}){return Fe(e,s,{locale:t,...o})},tryParse(s,o={}){return Ze(e,s,{locale:t,...o})},parseToParts(s,o={}){return He(e,s,{locale:t,...o})}}}export{Me as a,de as b,re as c,Fe as d,Ze as e,He as f,rn as g};
35
+ //# sourceMappingURL=chunk-YB6YAG7E.js.map