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.
- package/dist/calendarUtils.cjs +4 -259
- package/dist/calendarUtils.cjs.map +1 -1
- package/dist/calendarUtils.js +1 -33
- package/dist/chunk-2JHEV7ZF.js +7 -0
- package/dist/chunk-2JHEV7ZF.js.map +1 -0
- package/dist/chunk-3MZLTVP3.js +2 -0
- package/dist/{chunk-MXXYIMFJ.js.map → chunk-3MZLTVP3.js.map} +1 -1
- package/dist/chunk-4JWGUR4O.js +11 -0
- package/dist/{chunk-AXQLUIDY.js.map → chunk-4JWGUR4O.js.map} +1 -1
- package/dist/chunk-5U5WJ465.js +36 -0
- package/dist/chunk-5U5WJ465.js.map +1 -0
- package/dist/chunk-C5YESWFT.js +2 -0
- package/dist/chunk-C5YESWFT.js.map +1 -0
- package/dist/chunk-C6ZZ62ET.js +2 -0
- package/dist/{chunk-YS52LOMJ.js.map → chunk-C6ZZ62ET.js.map} +1 -1
- package/dist/chunk-EHLRZULM.js +5 -0
- package/dist/{chunk-4ZBGVYLR.js.map → chunk-EHLRZULM.js.map} +1 -1
- package/dist/chunk-F4RGUDA3.js +2 -0
- package/dist/chunk-F4RGUDA3.js.map +1 -0
- package/dist/chunk-J2AQNXF2.js +2 -0
- package/dist/{chunk-GRJQHQLW.js.map → chunk-J2AQNXF2.js.map} +1 -1
- package/dist/chunk-NBXF7V5B.js +2 -0
- package/dist/chunk-NBXF7V5B.js.map +1 -0
- package/dist/chunk-R52YOKI3.js +7 -0
- package/dist/{chunk-M33NSC4E.js.map → chunk-R52YOKI3.js.map} +1 -1
- package/dist/chunk-UJR6DHXY.js +2 -0
- package/dist/chunk-UJR6DHXY.js.map +1 -0
- package/dist/chunk-VQXUMFB2.js +21 -0
- package/dist/{chunk-VHSZL3P3.js.map → chunk-VQXUMFB2.js.map} +1 -1
- package/dist/chunk-XH46JNPK.js +2 -0
- package/dist/chunk-XH46JNPK.js.map +1 -0
- package/dist/chunk-YB6YAG7E.js +35 -0
- package/dist/chunk-YB6YAG7E.js.map +1 -0
- package/dist/chunk-ZGMTY52O.js +2 -0
- package/dist/chunk-ZGMTY52O.js.map +1 -0
- package/dist/duration.cjs +9 -624
- package/dist/duration.cjs.map +1 -1
- package/dist/duration.js +1 -29
- package/dist/format.cjs +18 -769
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +1 -20
- package/dist/formatDuration.d.cts +6 -0
- package/dist/formatDuration.d.ts +6 -0
- package/dist/index.cjs +146 -6672
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +58 -3004
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +18 -952
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +1 -41
- package/dist/localeRegistry.cjs +15 -328
- package/dist/localeRegistry.cjs.map +1 -1
- package/dist/localeRegistry.js +1 -11
- package/dist/localeVocab.d.cts +3 -0
- package/dist/localeVocab.d.ts +3 -0
- package/dist/parse.cjs +53 -1637
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +1 -20
- package/dist/parsePattern.d.cts +1 -0
- package/dist/parsePattern.d.ts +1 -0
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/recurrence.cjs +20 -450
- package/dist/recurrence.cjs.map +1 -1
- package/dist/recurrence.js +1 -20
- package/dist/relativeTime.cjs +24 -177
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +1 -14
- package/dist/rounding.d.cts +1 -0
- package/dist/rounding.d.ts +1 -0
- package/dist/serialization.d.cts +1 -1
- package/dist/serialization.d.ts +1 -1
- package/dist/timezone.cjs +20 -387
- package/dist/timezone.cjs.map +1 -1
- package/dist/timezone.js +1 -26
- package/dist/typeGuards.d.cts +9 -9
- package/dist/typeGuards.d.ts +9 -9
- package/package.json +4 -2
- package/dist/chunk-4ZBGVYLR.js +0 -182
- package/dist/chunk-AXQLUIDY.js +0 -183
- package/dist/chunk-CKVHXFX7.js +0 -902
- package/dist/chunk-CKVHXFX7.js.map +0 -1
- package/dist/chunk-CSUIKL73.js +0 -215
- package/dist/chunk-CSUIKL73.js.map +0 -1
- package/dist/chunk-EDUFSRWJ.js +0 -9
- package/dist/chunk-EDUFSRWJ.js.map +0 -1
- package/dist/chunk-FQA4XJME.js +0 -57
- package/dist/chunk-FQA4XJME.js.map +0 -1
- package/dist/chunk-FTMLZ4EN.js +0 -196
- package/dist/chunk-FTMLZ4EN.js.map +0 -1
- package/dist/chunk-GRJQHQLW.js +0 -170
- package/dist/chunk-JCIMP43G.js +0 -602
- package/dist/chunk-JCIMP43G.js.map +0 -1
- package/dist/chunk-KDYK72Z2.js +0 -75
- package/dist/chunk-KDYK72Z2.js.map +0 -1
- package/dist/chunk-M33NSC4E.js +0 -169
- package/dist/chunk-MXXYIMFJ.js +0 -29
- package/dist/chunk-NFKQ3EXC.js +0 -188
- package/dist/chunk-NFKQ3EXC.js.map +0 -1
- package/dist/chunk-O6GVMBLW.js +0 -403
- package/dist/chunk-O6GVMBLW.js.map +0 -1
- package/dist/chunk-VHSZL3P3.js +0 -242
- package/dist/chunk-VSUCBUTC.js +0 -295
- package/dist/chunk-VSUCBUTC.js.map +0 -1
- package/dist/chunk-YS52LOMJ.js +0 -51
package/dist/parse.cjs
CHANGED
|
@@ -1,136 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/parse.ts
|
|
21
|
-
var parse_exports = {};
|
|
22
|
-
__export(parse_exports, {
|
|
23
|
-
compileParser: () => compileParser,
|
|
24
|
-
parse: () => parse,
|
|
25
|
-
parseToParts: () => parseToParts,
|
|
26
|
-
safeParse: () => safeParse,
|
|
27
|
-
tryParse: () => tryParse
|
|
28
|
-
});
|
|
29
|
-
module.exports = __toCommonJS(parse_exports);
|
|
30
|
-
|
|
31
|
-
// src/temporalProvider.ts
|
|
32
|
-
var injectedTemporal;
|
|
33
|
-
var onTemporalChanged = [];
|
|
34
|
-
function subscribeToTemporalChanges(listener) {
|
|
35
|
-
onTemporalChanged.push(listener);
|
|
36
|
-
}
|
|
37
|
-
function resolveTemporal() {
|
|
38
|
-
return injectedTemporal ?? globalThis.Temporal;
|
|
39
|
-
}
|
|
40
|
-
function getTemporal() {
|
|
41
|
-
const temporal = resolveTemporal();
|
|
42
|
-
if (!temporal) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
"temporal-fmt: parse() needs a Temporal implementation to construct its result. Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal (native on Node 26+, or a polyfill like temporal-polyfill)."
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return temporal;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// src/errors.ts
|
|
51
|
-
var TemporalFmtError = class extends Error {
|
|
52
|
-
code;
|
|
53
|
-
input;
|
|
54
|
-
format;
|
|
55
|
-
token;
|
|
56
|
-
position;
|
|
57
|
-
expected;
|
|
58
|
-
actual;
|
|
59
|
-
reason;
|
|
60
|
-
constructor(message, fields) {
|
|
61
|
-
super(message);
|
|
62
|
-
this.name = "TemporalFmtError";
|
|
63
|
-
this.code = fields.code;
|
|
64
|
-
this.input = fields.input;
|
|
65
|
-
this.format = fields.format;
|
|
66
|
-
this.token = fields.token;
|
|
67
|
-
this.position = fields.position;
|
|
68
|
-
this.expected = fields.expected;
|
|
69
|
-
this.actual = fields.actual;
|
|
70
|
-
this.reason = fields.reason;
|
|
71
|
-
const capture = Error.captureStackTrace;
|
|
72
|
-
if (typeof capture === "function") {
|
|
73
|
-
capture(this, this.constructor);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Lets callers do `err.toJSON()` for logging. Plain `Error` doesn't
|
|
77
|
-
// serialize its non-enumerable fields; this picks them up explicitly.
|
|
78
|
-
toJSON() {
|
|
79
|
-
return {
|
|
80
|
-
name: this.name,
|
|
81
|
-
message: this.message,
|
|
82
|
-
code: this.code,
|
|
83
|
-
input: this.input,
|
|
84
|
-
format: this.format,
|
|
85
|
-
token: this.token,
|
|
86
|
-
position: this.position,
|
|
87
|
-
expected: this.expected,
|
|
88
|
-
actual: this.actual,
|
|
89
|
-
reason: this.reason
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
var FormatSyntaxError = class extends TemporalFmtError {
|
|
94
|
-
constructor(fields) {
|
|
95
|
-
const { message, ...rest } = fields;
|
|
96
|
-
super(
|
|
97
|
-
message ?? `format string "${fields.format ?? ""}" has a syntax error${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
98
|
-
{ code: "FORMAT_SYNTAX_ERROR", ...rest }
|
|
99
|
-
);
|
|
100
|
-
this.name = "FormatSyntaxError";
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
var UnknownTokenError = class extends TemporalFmtError {
|
|
104
|
-
constructor(fields) {
|
|
105
|
-
const { message, ...rest } = fields;
|
|
106
|
-
super(
|
|
107
|
-
message ?? `token "${fields.token ?? ""}" is not a recognized temporal-fmt token${fields.format ? ` in format string "${fields.format}"` : ""}.`,
|
|
108
|
-
{ code: "UNKNOWN_TOKEN", ...rest }
|
|
109
|
-
);
|
|
110
|
-
this.name = "UnknownTokenError";
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
var ParseMismatchError = class extends TemporalFmtError {
|
|
114
|
-
constructor(fields) {
|
|
115
|
-
const { message, ...rest } = fields;
|
|
116
|
-
super(
|
|
117
|
-
message ?? `input "${fields.input ?? ""}" does not match format "${fields.format ?? ""}"${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
118
|
-
{ code: "PARSE_MISMATCH", ...rest }
|
|
119
|
-
);
|
|
120
|
-
this.name = "ParseMismatchError";
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
var InvalidDateError = class extends TemporalFmtError {
|
|
124
|
-
constructor(fields) {
|
|
125
|
-
const { message, ...rest } = fields;
|
|
126
|
-
super(
|
|
127
|
-
message ?? `input "${fields.input ?? ""}" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
128
|
-
{ code: "INVALID_DATE", ...rest }
|
|
129
|
-
);
|
|
130
|
-
this.name = "InvalidDateError";
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
/* c8 ignore start @preserve -- InvalidTimeError is part of the public
|
|
1
|
+
"use strict";var pe=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var Ke=Object.getOwnPropertyNames;var We=Object.prototype.hasOwnProperty;var Qe=(e,t)=>{for(var n in t)pe(e,n,{get:t[n],enumerable:!0})},je=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Ke(t))!We.call(e,o)&&o!==n&&pe(e,o,{get:()=>t[o],enumerable:!(r=Ge(t,o))||r.enumerable});return e};var qe=e=>je(pe({},"__esModule",{value:!0}),e);var _t={};Qe(_t,{compileParser:()=>At,parse:()=>de,parseToParts:()=>Ze,safeParse:()=>ze,tryParse:()=>Ve});module.exports=qe(_t);var Be,Je=[];function De(e){Je.push(e)}function et(){return Be??globalThis.Temporal}function se(){let e=et();if(!e)throw new Error("temporal-fmt: parse() needs a Temporal implementation to construct its result. Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal (native on Node 26+, or a polyfill like temporal-polyfill).");return e}var E=class extends Error{code;input;format;token;position;expected;actual;reason;constructor(t,n){super(t),this.name="TemporalFmtError",this.code=n.code,this.input=n.input,this.format=n.format,this.token=n.token,this.position=n.position,this.expected=n.expected,this.actual=n.actual,this.reason=n.reason;let r=Error.captureStackTrace;typeof r=="function"&&r(this,this.constructor)}toJSON(){return{name:this.name,message:this.message,code:this.code,input:this.input,format:this.format,token:this.token,position:this.position,expected:this.expected,actual:this.actual,reason:this.reason}}},S=class extends E{constructor(t){let{message:n,...r}=t;super(n??`format string "${t.format??""}" has a syntax error${t.reason?`: ${t.reason}`:""}.`,{code:"FORMAT_SYNTAX_ERROR",...r}),this.name="FormatSyntaxError"}},I=class extends E{constructor(t){let{message:n,...r}=t;super(n??`token "${t.token??""}" is not a recognized temporal-fmt token${t.format?` in format string "${t.format}"`:""}.`,{code:"UNKNOWN_TOKEN",...r}),this.name="UnknownTokenError"}},T=class extends E{constructor(t){let{message:n,...r}=t;super(n??`input "${t.input??""}" does not match format "${t.format??""}"${t.reason?`: ${t.reason}`:""}.`,{code:"PARSE_MISMATCH",...r}),this.name="ParseMismatchError"}},L=class extends E{constructor(t){let{message:n,...r}=t;super(n??`input "${t.input??""}" does not describe a valid date${t.reason?`: ${t.reason}`:""}.`,{code:"INVALID_DATE",...r}),this.name="InvalidDateError"}};/* c8 ignore start @preserve -- InvalidTimeError is part of the public
|
|
134
2
|
error-class surface (exported from index.ts, code 'INVALID_TIME')
|
|
135
3
|
but nothing in this package constructs one. Investigated wiring it
|
|
136
4
|
in the same way InvalidTimeZoneError was just wired (see parse.ts's
|
|
@@ -142,29 +10,7 @@ var InvalidDateError = class extends TemporalFmtError {
|
|
|
142
10
|
is rejected as a plain shape mismatch before any semantic check
|
|
143
11
|
could run. There's no live gap to hook this into without inventing a
|
|
144
12
|
redundant check purely to give this class a body. Left unconstructed
|
|
145
|
-
until the library actually has a real invalid-time case to report. */
|
|
146
|
-
/* c8 ignore stop @preserve */
|
|
147
|
-
var InvalidOffsetError = class extends TemporalFmtError {
|
|
148
|
-
constructor(fields) {
|
|
149
|
-
const { message, ...rest } = fields;
|
|
150
|
-
super(
|
|
151
|
-
message ?? `offset "${fields.actual ?? ""}" is invalid${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
152
|
-
{ code: "INVALID_OFFSET", ...rest }
|
|
153
|
-
);
|
|
154
|
-
this.name = "InvalidOffsetError";
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
var InvalidTimeZoneError = class extends TemporalFmtError {
|
|
158
|
-
constructor(fields) {
|
|
159
|
-
const { message, ...rest } = fields;
|
|
160
|
-
super(
|
|
161
|
-
message ?? `time zone "${fields.actual ?? ""}" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ""}.`,
|
|
162
|
-
{ code: "INVALID_TIME_ZONE", ...rest }
|
|
163
|
-
);
|
|
164
|
-
this.name = "InvalidTimeZoneError";
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
/* c8 ignore start @preserve -- InvalidCalendarError is part of the
|
|
13
|
+
until the library actually has a real invalid-time case to report. *//* c8 ignore stop @preserve */var F=class extends E{constructor(t){let{message:n,...r}=t;super(n??`offset "${t.actual??""}" is invalid${t.reason?`: ${t.reason}`:""}.`,{code:"INVALID_OFFSET",...r}),this.name="InvalidOffsetError"}},G=class extends E{constructor(t){let{message:n,...r}=t;super(n??`time zone "${t.actual??""}" is not a recognized IANA time zone or fixed offset${t.reason?`: ${t.reason}`:""}.`,{code:"INVALID_TIME_ZONE",...r}),this.name="InvalidTimeZoneError"}};/* c8 ignore start @preserve -- InvalidCalendarError is part of the
|
|
168
14
|
public error-class surface (exported from index.ts, code
|
|
169
15
|
'INVALID_CALENDAR') but nothing in this package constructs one.
|
|
170
16
|
Checked for a wiring opportunity the same way InvalidTimeZoneError
|
|
@@ -174,1485 +20,55 @@ var InvalidTimeZoneError = class extends TemporalFmtError {
|
|
|
174
20
|
of the locale string — it's never handed an arbitrary "calendar"
|
|
175
21
|
value a caller could get wrong. There's no live input to reject.
|
|
176
22
|
Left unconstructed until the library actually accepts a calendar
|
|
177
|
-
parameter that could be invalid. */
|
|
178
|
-
/* c8 ignore stop @preserve */
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
var InvalidLocaleError = class extends TemporalFmtError {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
/* c8 ignore stop @preserve */
|
|
229
|
-
|
|
230
|
-
// src/localeVocab.ts
|
|
231
|
-
var customVocabs = /* @__PURE__ */ new Map();
|
|
232
|
-
function canonicalCacheKey(locale) {
|
|
233
|
-
try {
|
|
234
|
-
return new Intl.Locale(locale.replace(/_/g, "-")).toString().toLowerCase();
|
|
235
|
-
} catch {
|
|
236
|
-
return locale;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
var vocabCache = /* @__PURE__ */ new Map();
|
|
240
|
-
var MAX_VOCAB_CACHE_SIZE = 500;
|
|
241
|
-
function partValue(formatter, date, type) {
|
|
242
|
-
const parts = formatter.formatToParts(date);
|
|
243
|
-
const index = parts.findIndex((p) => p.type === type);
|
|
244
|
-
/* c8 ignore start @preserve -- defensive guard against a real but
|
|
245
|
-
unreproducible failure mode: an Intl implementation that omits the
|
|
246
|
-
requested part type entirely for some locale. Checked every locale
|
|
247
|
-
with unusual dayPeriod/weekday/month rendering available in this
|
|
248
|
-
runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)
|
|
249
|
-
against the exact formatter options getLocaleVocab uses (notably
|
|
250
|
-
hour12: true for the dayPeriod formatter, which is what makes every
|
|
251
|
-
locale here actually emit a dayPeriod part — omitting it is what
|
|
252
|
-
produced a false "gap" during investigation). None omit their part
|
|
253
|
-
on this runtime. A different ICU version or a non-Node Intl
|
|
254
|
-
implementation could plausibly behave differently, so this stays a
|
|
255
|
-
real check rather than an assertion. */
|
|
256
|
-
/* c8 ignore start @preserve */
|
|
257
|
-
if (index === -1) {
|
|
258
|
-
throw new InvalidLocaleError({
|
|
259
|
-
message: `temporal-fmt: locale produced no "${type}" part while building match vocabulary.`
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
/* c8 ignore stop @preserve */
|
|
263
|
-
let value = parts[index].value;
|
|
264
|
-
const prev = parts[index - 1];
|
|
265
|
-
const next = parts[index + 1];
|
|
266
|
-
if (prev?.type === "literal" && !/\s/.test(prev.value)) value = prev.value + value;
|
|
267
|
-
if (next?.type === "literal" && !/\s/.test(next.value)) value = value + next.value;
|
|
268
|
-
return value;
|
|
269
|
-
}
|
|
270
|
-
function assertNoCollision(names, label, locale) {
|
|
271
|
-
const seen = /* @__PURE__ */ new Map();
|
|
272
|
-
for (let i = 0; i < names.length; i++) {
|
|
273
|
-
const prior = seen.get(names[i]);
|
|
274
|
-
if (prior !== void 0) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
`temporal-fmt: locale "${locale}" renders ${label} index ${prior} and ${i} identically ("${names[i]}"). parse() can't reliably tell these apart for this locale/token, so this combination isn't supported.`
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
seen.set(names[i], i);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function getCustomVocab(locale) {
|
|
283
|
-
const cacheKey = canonicalCacheKey(locale);
|
|
284
|
-
return customVocabs.get(cacheKey);
|
|
285
|
-
}
|
|
286
|
-
function getLocaleVocab(locale) {
|
|
287
|
-
const cacheKey = canonicalCacheKey(locale);
|
|
288
|
-
const custom = customVocabs.get(cacheKey);
|
|
289
|
-
if (custom) {
|
|
290
|
-
return custom;
|
|
291
|
-
}
|
|
292
|
-
const cached = vocabCache.get(cacheKey);
|
|
293
|
-
if (cached) {
|
|
294
|
-
return cached;
|
|
295
|
-
}
|
|
296
|
-
const monthLongFmt = new Intl.DateTimeFormat(locale, { month: "long", timeZone: "UTC" });
|
|
297
|
-
const monthShortFmt = new Intl.DateTimeFormat(locale, { month: "short", timeZone: "UTC" });
|
|
298
|
-
const monthLong = [];
|
|
299
|
-
const monthShort = [];
|
|
300
|
-
for (let m = 0; m < 12; m++) {
|
|
301
|
-
const date = new Date(Date.UTC(2020, m, 1));
|
|
302
|
-
monthLong.push(partValue(monthLongFmt, date, "month"));
|
|
303
|
-
monthShort.push(partValue(monthShortFmt, date, "month"));
|
|
304
|
-
}
|
|
305
|
-
assertNoCollision(monthLong, "MMMM month", locale);
|
|
306
|
-
assertNoCollision(monthShort, "MMM month", locale);
|
|
307
|
-
const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: "long", timeZone: "UTC" });
|
|
308
|
-
const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
|
|
309
|
-
const weekdayLong = [];
|
|
310
|
-
const weekdayShort = [];
|
|
311
|
-
for (let d = 0; d < 7; d++) {
|
|
312
|
-
const date = new Date(Date.UTC(2024, 0, 1 + d));
|
|
313
|
-
weekdayLong.push(partValue(weekdayLongFmt, date, "weekday"));
|
|
314
|
-
weekdayShort.push(partValue(weekdayShortFmt, date, "weekday"));
|
|
315
|
-
}
|
|
316
|
-
assertNoCollision(weekdayLong, "EEEE weekday", locale);
|
|
317
|
-
assertNoCollision(weekdayShort, "EEE weekday", locale);
|
|
318
|
-
const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: "numeric", hour12: true, timeZone: "UTC" });
|
|
319
|
-
const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), "dayPeriod");
|
|
320
|
-
const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), "dayPeriod");
|
|
321
|
-
const dayPeriod = [.../* @__PURE__ */ new Set([am, pm])];
|
|
322
|
-
const vocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };
|
|
323
|
-
if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {
|
|
324
|
-
const oldestKey = vocabCache.keys().next().value;
|
|
325
|
-
if (oldestKey !== void 0) vocabCache.delete(oldestKey);
|
|
326
|
-
}
|
|
327
|
-
vocabCache.set(cacheKey, vocab);
|
|
328
|
-
return vocab;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// src/isoWeek.ts
|
|
332
|
-
var CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
|
|
333
|
-
function isGregorianLeapYear(year) {
|
|
334
|
-
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
335
|
-
}
|
|
336
|
-
function daysInYear(year) {
|
|
337
|
-
return isGregorianLeapYear(year) ? 366 : 365;
|
|
338
|
-
}
|
|
339
|
-
function dayOfYear(year, month, day) {
|
|
340
|
-
let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1] + day;
|
|
341
|
-
if (month > 2 && isGregorianLeapYear(year)) doy += 1;
|
|
342
|
-
return doy;
|
|
343
|
-
}
|
|
344
|
-
var REFERENCE_YEAR = 2e3;
|
|
345
|
-
function dayOfWeekOfJan1(year) {
|
|
346
|
-
let offset = 0;
|
|
347
|
-
if (year >= REFERENCE_YEAR) {
|
|
348
|
-
for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);
|
|
349
|
-
} else {
|
|
350
|
-
for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);
|
|
351
|
-
}
|
|
352
|
-
const zeroIndexed = ((6 - 1 + offset) % 7 + 7) % 7;
|
|
353
|
-
return zeroIndexed + 1;
|
|
354
|
-
}
|
|
355
|
-
function isoWeekYearAndWeek(year, month, day, dayOfWeek) {
|
|
356
|
-
const doy = dayOfYear(year, month, day);
|
|
357
|
-
const thursdayDoyRelative = doy + (4 - dayOfWeek);
|
|
358
|
-
let isoYear;
|
|
359
|
-
let thursdayDoy;
|
|
360
|
-
if (thursdayDoyRelative < 1) {
|
|
361
|
-
isoYear = year - 1;
|
|
362
|
-
thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);
|
|
363
|
-
} else if (thursdayDoyRelative > daysInYear(year)) {
|
|
364
|
-
isoYear = year + 1;
|
|
365
|
-
thursdayDoy = thursdayDoyRelative - daysInYear(year);
|
|
366
|
-
} else {
|
|
367
|
-
isoYear = year;
|
|
368
|
-
thursdayDoy = thursdayDoyRelative;
|
|
369
|
-
}
|
|
370
|
-
const jan1Dow = dayOfWeekOfJan1(isoYear);
|
|
371
|
-
const firstThursdayDoy = 1 + (4 - jan1Dow + 7) % 7;
|
|
372
|
-
const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);
|
|
373
|
-
return { isoYear, week };
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// src/tokens.ts
|
|
377
|
-
function pad(n, len) {
|
|
378
|
-
const negative = n < 0;
|
|
379
|
-
const digits = String(Math.abs(n)).padStart(len, "0");
|
|
380
|
-
return negative ? "-" + digits : digits;
|
|
381
|
-
}
|
|
382
|
-
pad.fraction = function formatFraction(t, width) {
|
|
383
|
-
const nanoOfSecond = t.millisecond * 1e6 + (t.microsecond ?? 0) * 1e3 + (t.nanosecond ?? 0);
|
|
384
|
-
return pad(nanoOfSecond, 9).slice(0, width);
|
|
385
|
-
};
|
|
386
|
-
var DEFAULT_LOCALE = "en-US";
|
|
387
|
-
var formatterCache = /* @__PURE__ */ new Map();
|
|
388
|
-
var MAX_CACHE_SIZE = 500;
|
|
389
|
-
function getFormatter(locale, options) {
|
|
390
|
-
const key = JSON.stringify([canonicalCacheKey(locale), options]);
|
|
391
|
-
let formatter = formatterCache.get(key);
|
|
392
|
-
if (formatter) {
|
|
393
|
-
return formatter;
|
|
394
|
-
}
|
|
395
|
-
if (formatterCache.size >= MAX_CACHE_SIZE) {
|
|
396
|
-
const oldestKey = formatterCache.keys().next().value;
|
|
397
|
-
if (oldestKey !== void 0) formatterCache.delete(oldestKey);
|
|
398
|
-
}
|
|
399
|
-
formatter = new Intl.DateTimeFormat(locale, options);
|
|
400
|
-
formatterCache.set(key, formatter);
|
|
401
|
-
return formatter;
|
|
402
|
-
}
|
|
403
|
-
var nativeSupport;
|
|
404
|
-
subscribeToTemporalChanges(() => {
|
|
405
|
-
nativeSupport = void 0;
|
|
406
|
-
});
|
|
407
|
-
function intlSupportsNativeTemporal() {
|
|
408
|
-
if (nativeSupport === void 0) {
|
|
409
|
-
nativeSupport = false;
|
|
410
|
-
try {
|
|
411
|
-
const temporal = getTemporal();
|
|
412
|
-
new Intl.DateTimeFormat("en-US", { day: "numeric" }).formatToParts(temporal.PlainDate.from({ year: 1970, month: 1, day: 1 }));
|
|
413
|
-
nativeSupport = true;
|
|
414
|
-
} catch {
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return nativeSupport;
|
|
418
|
-
}
|
|
419
|
-
function intlPart(temporal, locale, options, partType) {
|
|
420
|
-
const calendar = temporal?.calendarId;
|
|
421
|
-
const formatterOptions = {
|
|
422
|
-
...options,
|
|
423
|
-
calendar: calendar && calendar !== "iso8601" ? calendar : "gregory"
|
|
424
|
-
};
|
|
425
|
-
if (!intlSupportsNativeTemporal()) {
|
|
426
|
-
return temporal.toLocaleString(locale, formatterOptions);
|
|
427
|
-
}
|
|
428
|
-
const { toInstant, timeZoneId } = temporal;
|
|
429
|
-
const isZoned = typeof toInstant === "function" && typeof timeZoneId === "string";
|
|
430
|
-
const intlSafeTemporal = isZoned ? temporal.toInstant() : temporal;
|
|
431
|
-
const nativeOptions = {
|
|
432
|
-
...formatterOptions,
|
|
433
|
-
...isZoned ? { timeZone: timeZoneId } : {}
|
|
434
|
-
};
|
|
435
|
-
const formatter = getFormatter(locale, nativeOptions);
|
|
436
|
-
const parts = formatter.formatToParts(intlSafeTemporal);
|
|
437
|
-
const index = parts.findIndex((p) => p.type === partType);
|
|
438
|
-
if (index === -1) {
|
|
439
|
-
throw new Error(
|
|
440
|
-
`temporal-fmt: locale "${locale}" produced no "${partType}" part for this token. This usually means the Temporal object is missing the field the token needs.`
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
let value = parts[index].value;
|
|
444
|
-
const prev = parts[index - 1];
|
|
445
|
-
const next = parts[index + 1];
|
|
446
|
-
if (prev?.type === "literal" && !/\s/.test(prev.value)) value = prev.value + value;
|
|
447
|
-
if (next?.type === "literal" && !/\s/.test(next.value)) value = value + next.value;
|
|
448
|
-
return value;
|
|
449
|
-
}
|
|
450
|
-
function dayPeriodPart(hour, locale) {
|
|
451
|
-
const custom = getCustomVocab(locale);
|
|
452
|
-
if (custom) {
|
|
453
|
-
return hour < 12 ? custom.dayPeriod[0] : custom.dayPeriod[1];
|
|
454
|
-
}
|
|
455
|
-
const date = new Date(Date.UTC(1970, 0, 1, hour));
|
|
456
|
-
const formatter = getFormatter(locale, { hour: "numeric", hour12: true, timeZone: "UTC" });
|
|
457
|
-
const part = formatter.formatToParts(date).find((p) => p.type === "dayPeriod");
|
|
458
|
-
if (!part) {
|
|
459
|
-
throw new Error(`temporal-fmt: locale "${locale}" produced no "dayPeriod" part for token "a".`);
|
|
460
|
-
}
|
|
461
|
-
return part.value;
|
|
462
|
-
}
|
|
463
|
-
function localeAwareName(temporal, locale, options, partType, customArray, customIndex) {
|
|
464
|
-
if (customArray && customIndex !== void 0 && customIndex >= 0 && customIndex < customArray.length) {
|
|
465
|
-
return customArray[customIndex];
|
|
466
|
-
}
|
|
467
|
-
return intlPart(temporal, locale, options, partType);
|
|
468
|
-
}
|
|
469
|
-
function formatOffset(offset, variant) {
|
|
470
|
-
if (offset === "+00:00" && (variant === "X" || variant === "XX" || variant === "XXX")) {
|
|
471
|
-
return "Z";
|
|
472
|
-
}
|
|
473
|
-
const sign = offset[0];
|
|
474
|
-
const hours = offset.slice(1, 3);
|
|
475
|
-
const minutes = offset.slice(4, 6);
|
|
476
|
-
switch (variant) {
|
|
477
|
-
case "X":
|
|
478
|
-
case "x":
|
|
479
|
-
return minutes === "00" ? `${sign}${hours}` : `${sign}${hours}${minutes}`;
|
|
480
|
-
case "XX":
|
|
481
|
-
case "xx":
|
|
482
|
-
return `${sign}${hours}${minutes}`;
|
|
483
|
-
case "XXX":
|
|
484
|
-
case "xxx":
|
|
485
|
-
return `${sign}${hours}:${minutes}`;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
var TOKENS = [
|
|
489
|
-
["yyyy", (t) => pad(t.year, 4), "year"],
|
|
490
|
-
["yy", (t) => {
|
|
491
|
-
if (t.year < 0) {
|
|
492
|
-
throw new Error(
|
|
493
|
-
`temporal-fmt: token "yy" doesn't support negative years (got ${t.year}), since truncating to 2 digits would make it indistinguishable from a positive year. Use "yyyy" instead.`
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
return pad(t.year % 100, 2);
|
|
497
|
-
}, "year"],
|
|
498
|
-
["MMMM", (t, locale) => {
|
|
499
|
-
const custom = getCustomVocab(locale);
|
|
500
|
-
return localeAwareName(t, locale, { month: "long" }, "month", custom?.monthLong, t.month - 1);
|
|
501
|
-
}, "month"],
|
|
502
|
-
["MMM", (t, locale) => {
|
|
503
|
-
const custom = getCustomVocab(locale);
|
|
504
|
-
return localeAwareName(t, locale, { month: "short" }, "month", custom?.monthShort, t.month - 1);
|
|
505
|
-
}, "month"],
|
|
506
|
-
["MM", (t) => pad(t.month, 2), "month"],
|
|
507
|
-
["M", (t) => String(t.month), "month"],
|
|
508
|
-
["dd", (t) => pad(t.day, 2), "day"],
|
|
509
|
-
["d", (t) => String(t.day), "day"],
|
|
510
|
-
["EEEE", (t, locale) => {
|
|
511
|
-
const custom = getCustomVocab(locale);
|
|
512
|
-
return localeAwareName(t, locale, { weekday: "long" }, "weekday", custom?.weekdayLong, t.dayOfWeek - 1);
|
|
513
|
-
}, "dayOfWeek"],
|
|
514
|
-
["EEE", (t, locale) => {
|
|
515
|
-
const custom = getCustomVocab(locale);
|
|
516
|
-
return localeAwareName(t, locale, { weekday: "short" }, "weekday", custom?.weekdayShort, t.dayOfWeek - 1);
|
|
517
|
-
}, "dayOfWeek"],
|
|
518
|
-
["HH", (t) => pad(t.hour, 2), "hour"],
|
|
519
|
-
["H", (t) => String(t.hour), "hour"],
|
|
520
|
-
["hh", (t) => pad(t.hour % 12 || 12, 2), "hour"],
|
|
521
|
-
["h", (t) => String(t.hour % 12 || 12), "hour"],
|
|
522
|
-
["mm", (t) => pad(t.minute, 2), "minute"],
|
|
523
|
-
["m", (t) => String(t.minute), "minute"],
|
|
524
|
-
["ss", (t) => pad(t.second, 2), "second"],
|
|
525
|
-
["s", (t) => String(t.second), "second"],
|
|
526
|
-
// Fractional-second tokens, S through SSSSSSSSS (1-9 digits). Each token
|
|
527
|
-
// formats a slice of the same underlying nanosecond-of-second value —
|
|
528
|
-
// combining millisecond/microsecond/nanosecond into one 9-digit number
|
|
529
|
-
// and truncating to the token's width — so "SSS" keeps meaning exactly
|
|
530
|
-
// what it always meant (3-digit milliseconds) while wider tokens expose
|
|
531
|
-
// the precision Temporal actually carries. formatFraction below is the
|
|
532
|
-
// shared implementation; see its comment for the truncate-not-round
|
|
533
|
-
// rule and why.
|
|
534
|
-
["SSSSSSSSS", (t) => pad.fraction(t, 9), "millisecond"],
|
|
535
|
-
["SSSSSSSS", (t) => pad.fraction(t, 8), "millisecond"],
|
|
536
|
-
["SSSSSSS", (t) => pad.fraction(t, 7), "millisecond"],
|
|
537
|
-
["SSSSSS", (t) => pad.fraction(t, 6), "millisecond"],
|
|
538
|
-
["SSSSS", (t) => pad.fraction(t, 5), "millisecond"],
|
|
539
|
-
["SSSS", (t) => pad.fraction(t, 4), "millisecond"],
|
|
540
|
-
["SSS", (t) => pad.fraction(t, 3), "millisecond"],
|
|
541
|
-
["SS", (t) => pad.fraction(t, 2), "millisecond"],
|
|
542
|
-
["S", (t) => pad.fraction(t, 1), "millisecond"],
|
|
543
|
-
// dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but
|
|
544
|
-
// still needs .hour on the input to compute which period it is
|
|
545
|
-
["a", (t, locale) => dayPeriodPart(t.hour, locale), "hour"],
|
|
546
|
-
["zzz", (t) => t.timeZoneId, "timeZoneId"],
|
|
547
|
-
// Numeric UTC offset tokens (date-fns/Unicode-LDML family). Only
|
|
548
|
-
// ZonedDateTime carries an offset, so the field check in format() throws
|
|
549
|
-
// the same "requires offset, which this Temporal object doesn't have"
|
|
550
|
-
// error zzz throws on PlainDate/PlainTime/PlainDateTime — same
|
|
551
|
-
// validation path, just a different field name. See formatOffset above
|
|
552
|
-
// for the per-variant width and Z/numeric distinction.
|
|
553
|
-
["xxx", (t) => formatOffset(t.offset, "xxx"), "offset"],
|
|
554
|
-
["xx", (t) => formatOffset(t.offset, "xx"), "offset"],
|
|
555
|
-
["X", (t) => formatOffset(t.offset, "X"), "offset"],
|
|
556
|
-
["XX", (t) => formatOffset(t.offset, "XX"), "offset"],
|
|
557
|
-
["XXX", (t) => formatOffset(t.offset, "XXX"), "offset"],
|
|
558
|
-
["x", (t) => formatOffset(t.offset, "x"), "offset"],
|
|
559
|
-
// Ordinal day (1st, 2nd, 3rd, ... 21st). English suffix rules only —
|
|
560
|
-
// locale-aware ordinals ("2." in de-DE, "2日" in ja-JP) are out of scope,
|
|
561
|
-
// since the rest of this library routes locale-specific names through
|
|
562
|
-
// Intl.DateTimeFormat, and Intl has no part type for ordinals. Format-only:
|
|
563
|
-
// the suffix isn't structurally distinguishable from a literal in a parse
|
|
564
|
-
// context (a "st"/"nd"/"rd"/"th" suffix isn't a digit and would collide
|
|
565
|
-
// with any adjacent literal text), so there's no good way to read it back.
|
|
566
|
-
["do", (t) => {
|
|
567
|
-
const day = t.day;
|
|
568
|
-
const lastDigit = day % 10;
|
|
569
|
-
const lastTwoDigits = day % 100;
|
|
570
|
-
if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
|
|
571
|
-
return day + "th";
|
|
572
|
-
}
|
|
573
|
-
if (lastDigit === 1) return day + "st";
|
|
574
|
-
if (lastDigit === 2) return day + "nd";
|
|
575
|
-
if (lastDigit === 3) return day + "rd";
|
|
576
|
-
return day + "th";
|
|
577
|
-
}, "day"],
|
|
578
|
-
// Quarter computed from month: 1-3=Q1, 4-6=Q2, 7-9=Q3, 10-12=Q4.
|
|
579
|
-
// `Q` is plain numeric, `QQQ` renders as "Q3" — same convention as
|
|
580
|
-
// date-fns's `Q` and `QQQ` for parity with the most common prior art.
|
|
581
|
-
// Both format and parse; parse() cross-checks Q/QQQ against the parsed
|
|
582
|
-
// month in the same spirit as the EEEE-vs-date cross-check.
|
|
583
|
-
["Q", (t) => String(Math.ceil(t.month / 3)), "month"],
|
|
584
|
-
["QQQ", (t) => "Q" + Math.ceil(t.month / 3), "month"],
|
|
585
|
-
// ISO 8601 week and week-numbering year. Both are format-only — parsing
|
|
586
|
-
// "ww"/"RRRR" back into a real date requires resolving an ISO week + a
|
|
587
|
-
// weekday (or some other disambiguator) to a specific date, which is a
|
|
588
|
-
// different parsing surface than the token-based parse() here. The
|
|
589
|
-
// ISO-week year (RRRR) can differ from the calendar year at the boundary:
|
|
590
|
-
// Dec 29-31 often belong to week 1 of the *next* year; Jan 1-3 often
|
|
591
|
-
// belong to week 52/53 of the *previous* year. See isoWeekYearAndWeek().
|
|
592
|
-
["ww", (t) => {
|
|
593
|
-
const { week } = isoWeekYearAndWeek(t.year, t.month, t.day, t.dayOfWeek);
|
|
594
|
-
return pad(week, 2);
|
|
595
|
-
}, "dayOfWeek"],
|
|
596
|
-
["RRRR", (t) => {
|
|
597
|
-
const { isoYear } = isoWeekYearAndWeek(t.year, t.month, t.day, t.dayOfWeek);
|
|
598
|
-
return pad(isoYear, 4);
|
|
599
|
-
}, "dayOfWeek"],
|
|
600
|
-
// Day of year — number of days since Jan 1 (1-366). Three widths:
|
|
601
|
-
// D — unpadded (1, 2, 366)
|
|
602
|
-
// DD — 2-digit minimum (zero-padded if <100)
|
|
603
|
-
// DDD — 3-digit zero-padded (001, 002, 366)
|
|
604
|
-
// Format-only — parsing day-of-year requires resolving against a year,
|
|
605
|
-
// which is a different shape from the token-based parse() surface.
|
|
606
|
-
// The dayOfYearHelper() in calendarUtils.ts covers the same field for
|
|
607
|
-
// callers who need the numeric value.
|
|
608
|
-
["D", (t) => String(dayOfYear(t.year, t.month, t.day)), "day"],
|
|
609
|
-
["DD", (t) => pad(dayOfYear(t.year, t.month, t.day), 2), "day"],
|
|
610
|
-
["DDD", (t) => pad(dayOfYear(t.year, t.month, t.day), 3), "day"],
|
|
611
|
-
// Stand-alone month — uses Intl's stand-alone form. In most locales
|
|
612
|
-
// (en, fr, de, es) this is identical to MMMM/MMM. In Slavic/Baltic
|
|
613
|
-
// locales (cs, sk, pl, ru) the stand-alone form differs from the
|
|
614
|
-
// format form (nominative vs genitive case). LLLL = long, LLL = short.
|
|
615
|
-
["LLLL", (t, locale) => {
|
|
616
|
-
const custom = getCustomVocab(locale);
|
|
617
|
-
return localeAwareName(t, locale, { month: "long" }, "month", custom?.monthLong, t.month - 1);
|
|
618
|
-
}, "month"],
|
|
619
|
-
["LLL", (t, locale) => {
|
|
620
|
-
const custom = getCustomVocab(locale);
|
|
621
|
-
return localeAwareName(t, locale, { month: "short" }, "month", custom?.monthShort, t.month - 1);
|
|
622
|
-
}, "month"],
|
|
623
|
-
// Stand-alone weekday — same pattern as stand-alone month but for
|
|
624
|
-
// weekday names. cccc = long, ccc = short.
|
|
625
|
-
["cccc", (t, locale) => {
|
|
626
|
-
const custom = getCustomVocab(locale);
|
|
627
|
-
return localeAwareName(t, locale, { weekday: "long" }, "weekday", custom?.weekdayLong, t.dayOfWeek - 1);
|
|
628
|
-
}, "dayOfWeek"],
|
|
629
|
-
["ccc", (t, locale) => {
|
|
630
|
-
const custom = getCustomVocab(locale);
|
|
631
|
-
return localeAwareName(t, locale, { weekday: "short" }, "weekday", custom?.weekdayShort, t.dayOfWeek - 1);
|
|
632
|
-
}, "dayOfWeek"],
|
|
633
|
-
// Era — locale-aware ("AD"/"BC" in en, "ap. J.-C."/"av. J.-C." in fr).
|
|
634
|
-
// GGGG = long, G = short. Format-only.
|
|
635
|
-
["GGGG", (t, locale) => {
|
|
636
|
-
return intlPart(t, locale, { era: "long" }, "era");
|
|
637
|
-
}, "year"],
|
|
638
|
-
["G", (t, locale) => {
|
|
639
|
-
return intlPart(t, locale, { era: "short" }, "era");
|
|
640
|
-
}, "year"],
|
|
641
|
-
// Localized timezone name — uses Intl's longLocalized/short timezone
|
|
642
|
-
// name option. Format-only — these names are locale-dependent and
|
|
643
|
-
// vary by season (EST vs EDT), so parsing them back requires a
|
|
644
|
-
// lookup table that isn't practical to ship.
|
|
645
|
-
["zzzz", (t, locale) => {
|
|
646
|
-
return intlPart(t, locale, { timeZoneName: "longGeneric" }, "timeZoneName");
|
|
647
|
-
}, "timeZoneId"],
|
|
648
|
-
["z", (t, locale) => {
|
|
649
|
-
return intlPart(t, locale, { timeZoneName: "short" }, "timeZoneName");
|
|
650
|
-
}, "timeZoneId"]
|
|
651
|
-
];
|
|
652
|
-
|
|
653
|
-
// src/tokenize.ts
|
|
654
|
-
var SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);
|
|
655
|
-
function tokenize(format) {
|
|
656
|
-
const pieces = [];
|
|
657
|
-
let i = 0;
|
|
658
|
-
while (i < format.length) {
|
|
659
|
-
const ch = format[i];
|
|
660
|
-
if (ch === "'") {
|
|
661
|
-
if (format[i + 1] === "'") {
|
|
662
|
-
appendLiteral(pieces, "'");
|
|
663
|
-
i += 2;
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
let j = i + 1;
|
|
667
|
-
let literal = "";
|
|
668
|
-
let closed = false;
|
|
669
|
-
while (j < format.length) {
|
|
670
|
-
if (format[j] === "'") {
|
|
671
|
-
if (format[j + 1] === "'") {
|
|
672
|
-
literal += "'";
|
|
673
|
-
j += 2;
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
closed = true;
|
|
677
|
-
j += 1;
|
|
678
|
-
break;
|
|
679
|
-
}
|
|
680
|
-
literal += format[j];
|
|
681
|
-
j += 1;
|
|
682
|
-
}
|
|
683
|
-
if (!closed) {
|
|
684
|
-
throw new FormatSyntaxError({
|
|
685
|
-
format,
|
|
686
|
-
message: `temporal-fmt: unterminated quote in format string "${format}"`
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
appendLiteral(pieces, literal);
|
|
690
|
-
i = j;
|
|
691
|
-
continue;
|
|
692
|
-
}
|
|
693
|
-
const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));
|
|
694
|
-
if (match) {
|
|
695
|
-
const runChar = match[match.length - 1];
|
|
696
|
-
if (format[i + match.length] === runChar) {
|
|
697
|
-
let end = i + match.length;
|
|
698
|
-
while (format[end] === runChar) end += 1;
|
|
699
|
-
throw new UnknownTokenError({
|
|
700
|
-
format,
|
|
701
|
-
token: format.slice(i, end),
|
|
702
|
-
message: `temporal-fmt: "${format.slice(i, end)}" in format string "${format}" isn't a recognized token \u2014 did you mean "${match}"?`
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
pieces.push({ kind: "token", value: match });
|
|
706
|
-
i += match.length;
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
appendLiteral(pieces, ch);
|
|
710
|
-
i += 1;
|
|
711
|
-
}
|
|
712
|
-
return pieces;
|
|
713
|
-
}
|
|
714
|
-
function appendLiteral(pieces, value) {
|
|
715
|
-
const last = pieces[pieces.length - 1];
|
|
716
|
-
if (last && last.kind === "literal") {
|
|
717
|
-
last.value += value;
|
|
718
|
-
} else {
|
|
719
|
-
pieces.push({ kind: "literal", value });
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// src/pattern.ts
|
|
724
|
-
function escapeRegExp(literal) {
|
|
725
|
-
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
726
|
-
}
|
|
727
|
-
function alternation(values, caseInsensitive = false) {
|
|
728
|
-
const escaped = values.map(escapeRegExp);
|
|
729
|
-
if (!caseInsensitive) return `(?:${escaped.join("|")})`;
|
|
730
|
-
return `(?:${escaped.map(foldCase).join("|")})`;
|
|
731
|
-
}
|
|
732
|
-
function foldCase(value) {
|
|
733
|
-
return value.replace(/[a-zA-Z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);
|
|
734
|
-
}
|
|
735
|
-
var TIME_ZONE_SHAPE = "(?:UTC|[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\d{1,2})?(?:\\/[A-Za-z0-9_+-]+)*)";
|
|
736
|
-
var OFFSET_SHAPES = {
|
|
737
|
-
X: "(?:Z|[+-]\\d{2}(?:\\d{2})?)",
|
|
738
|
-
XX: "(?:Z|[+-]\\d{4})",
|
|
739
|
-
XXX: "(?:Z|[+-]\\d{2}:\\d{2})",
|
|
740
|
-
x: "[+-]\\d{2}(?:\\d{2})?",
|
|
741
|
-
xx: "[+-]\\d{4}",
|
|
742
|
-
xxx: "[+-]\\d{2}:\\d{2}"
|
|
743
|
-
};
|
|
744
|
-
function getTimeZoneFragment() {
|
|
745
|
-
return TIME_ZONE_SHAPE;
|
|
746
|
-
}
|
|
747
|
-
var validZoneSet;
|
|
748
|
-
function getValidZoneSet() {
|
|
749
|
-
if (!validZoneSet) {
|
|
750
|
-
validZoneSet = new Set(Intl.supportedValuesOf("timeZone"));
|
|
751
|
-
validZoneSet.add("UTC");
|
|
752
|
-
}
|
|
753
|
-
return validZoneSet;
|
|
754
|
-
}
|
|
755
|
-
var FIXED_OFFSET_RE = /^[+-]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;
|
|
756
|
-
function isValidTimeZone(raw) {
|
|
757
|
-
return FIXED_OFFSET_RE.test(raw) || getValidZoneSet().has(raw);
|
|
758
|
-
}
|
|
759
|
-
var NUMERIC_FRAGMENTS = {
|
|
760
|
-
yy: "\\d{2}",
|
|
761
|
-
MM: "(?:0[1-9]|1[0-2])",
|
|
762
|
-
M: "(?:1[0-2]|[1-9])",
|
|
763
|
-
dd: "(?:0[1-9]|[12]\\d|3[01])",
|
|
764
|
-
d: "(?:[12]\\d|3[01]|[1-9])",
|
|
765
|
-
HH: "(?:[01]\\d|2[0-3])",
|
|
766
|
-
H: "(?:1\\d|2[0-3]|[0-9])",
|
|
767
|
-
hh: "(?:0[1-9]|1[0-2])",
|
|
768
|
-
h: "(?:1[0-2]|[1-9])",
|
|
769
|
-
mm: "(?:[0-5]\\d)",
|
|
770
|
-
m: "(?:[1-5]\\d|[0-9])",
|
|
771
|
-
ss: "(?:[0-5]\\d)",
|
|
772
|
-
s: "(?:[1-5]\\d|[0-9])",
|
|
773
|
-
SSSSSSSSS: "\\d{9}",
|
|
774
|
-
SSSSSSSS: "\\d{8}",
|
|
775
|
-
SSSSSSS: "\\d{7}",
|
|
776
|
-
SSSSSS: "\\d{6}",
|
|
777
|
-
SSSSS: "\\d{5}",
|
|
778
|
-
SSSS: "\\d{4}",
|
|
779
|
-
SSS: "\\d{3}",
|
|
780
|
-
SS: "\\d{2}",
|
|
781
|
-
S: "\\d",
|
|
782
|
-
// Q is always a single digit 1-4 (no padding variant, so no width ambiguity
|
|
783
|
-
// with adjacent digit tokens the way M/d/H/m/s have).
|
|
784
|
-
Q: "[1-4]"
|
|
785
|
-
};
|
|
786
|
-
var QQQ_FRAGMENT = "Q[1-4]";
|
|
787
|
-
var FORMAT_ONLY_TOKENS = /* @__PURE__ */ new Set(["do", "ww", "RRRR", "D", "DD", "DDD", "LLLL", "LLL", "cccc", "ccc", "GGGG", "G", "zzzz", "z"]);
|
|
788
|
-
var YYYY_EXACT = "-?\\d{4}";
|
|
789
|
-
var YYYY_EXTENDED = "-?\\d{4,}";
|
|
790
|
-
var DIGIT_LEADING_TOKENS = /* @__PURE__ */ new Set([
|
|
791
|
-
"yyyy",
|
|
792
|
-
"yy",
|
|
793
|
-
"MM",
|
|
794
|
-
"M",
|
|
795
|
-
"dd",
|
|
796
|
-
"d",
|
|
797
|
-
"HH",
|
|
798
|
-
"H",
|
|
799
|
-
"hh",
|
|
800
|
-
"h",
|
|
801
|
-
"mm",
|
|
802
|
-
"m",
|
|
803
|
-
"ss",
|
|
804
|
-
"s",
|
|
805
|
-
"SSSSSSSSS",
|
|
806
|
-
"SSSSSSSS",
|
|
807
|
-
"SSSSSSS",
|
|
808
|
-
"SSSSSS",
|
|
809
|
-
"SSSSS",
|
|
810
|
-
"SSSS",
|
|
811
|
-
"SSS",
|
|
812
|
-
"SS",
|
|
813
|
-
"S"
|
|
814
|
-
]);
|
|
815
|
-
function tokenFragment(token, locale, nextToken) {
|
|
816
|
-
if (token === "yyyy") {
|
|
817
|
-
return nextToken !== void 0 && DIGIT_LEADING_TOKENS.has(nextToken) ? YYYY_EXACT : YYYY_EXTENDED;
|
|
818
|
-
}
|
|
819
|
-
const numeric = NUMERIC_FRAGMENTS[token];
|
|
820
|
-
if (numeric) {
|
|
821
|
-
return numeric;
|
|
822
|
-
}
|
|
823
|
-
if (token === "QQQ") {
|
|
824
|
-
return QQQ_FRAGMENT;
|
|
825
|
-
}
|
|
826
|
-
if (FORMAT_ONLY_TOKENS.has(token)) {
|
|
827
|
-
throw new UnknownTokenError({
|
|
828
|
-
token,
|
|
829
|
-
message: `temporal-fmt: token "${token}" 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").`
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
const vocab = getLocaleVocab(locale);
|
|
833
|
-
switch (token) {
|
|
834
|
-
case "MMMM":
|
|
835
|
-
return alternation(vocab.monthLong);
|
|
836
|
-
case "MMM":
|
|
837
|
-
return alternation(vocab.monthShort);
|
|
838
|
-
case "EEEE":
|
|
839
|
-
return alternation(vocab.weekdayLong);
|
|
840
|
-
case "EEE":
|
|
841
|
-
return alternation(vocab.weekdayShort);
|
|
842
|
-
// Case-insensitive on purpose: "pm"/"Pm"/"PM" all mean the same thing,
|
|
843
|
-
// and unlike the Md-glue ambiguity elsewhere in this file, there's no
|
|
844
|
-
// second valid reading to guess wrong — so rejecting on case buys no
|
|
845
|
-
// correctness, only friction against real-world data (mixed-case CSV
|
|
846
|
-
// exports, lowercase log timestamps).
|
|
847
|
-
case "a":
|
|
848
|
-
return alternation(vocab.dayPeriod, true);
|
|
849
|
-
case "zzz":
|
|
850
|
-
return getTimeZoneFragment();
|
|
851
|
-
case "X":
|
|
852
|
-
case "XX":
|
|
853
|
-
case "XXX":
|
|
854
|
-
case "x":
|
|
855
|
-
case "xx":
|
|
856
|
-
case "xxx":
|
|
857
|
-
return OFFSET_SHAPES[token];
|
|
858
|
-
/* c8 ignore start @preserve -- defensive guard, not reachable through
|
|
859
|
-
the public API. tokenFragment's only caller (buildCapturingPattern
|
|
860
|
-
in parsePattern.ts) always passes piece.value straight from
|
|
861
|
-
tokenize.ts, which only ever emits strings from tokens.ts's TOKENS
|
|
862
|
-
table. Every token in that table is handled above: the numeric
|
|
863
|
-
ones via NUMERIC_FRAGMENTS, QQQ via QQQ_FRAGMENT, the format-only
|
|
864
|
-
ones via the FORMAT_ONLY_TOKENS check earlier in this function, and
|
|
865
|
-
everything else via one of the switch cases. There's no token
|
|
866
|
-
string that can reach this default without either tokens.ts
|
|
867
|
-
registering something new or tokenize.ts being bypassed, neither of
|
|
868
|
-
which happens on the parse() path. */
|
|
869
|
-
default:
|
|
870
|
-
throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token "${token}"` });
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
var UNPADDED_NUMERIC_TOKENS = /* @__PURE__ */ new Set(["M", "d", "H", "h", "m", "s"]);
|
|
874
|
-
var UNPADDED_NUMERIC_RANGES = {
|
|
875
|
-
M: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],
|
|
876
|
-
d: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 31 }],
|
|
877
|
-
H: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 23 }],
|
|
878
|
-
h: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],
|
|
879
|
-
m: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],
|
|
880
|
-
s: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }]
|
|
881
|
-
};
|
|
882
|
-
function enumerateValidSplits(digits, tokens) {
|
|
883
|
-
const memo = /* @__PURE__ */ new Map();
|
|
884
|
-
function solve(tokenIndex, offset) {
|
|
885
|
-
const key = `${tokenIndex}:${offset}`;
|
|
886
|
-
const cached = memo.get(key);
|
|
887
|
-
if (cached) {
|
|
888
|
-
return cached;
|
|
889
|
-
}
|
|
890
|
-
if (tokenIndex === tokens.length) {
|
|
891
|
-
const result = offset === digits.length ? [[]] : [];
|
|
892
|
-
memo.set(key, result);
|
|
893
|
-
return result;
|
|
894
|
-
}
|
|
895
|
-
const token = tokens[tokenIndex];
|
|
896
|
-
const ranges = UNPADDED_NUMERIC_RANGES[token];
|
|
897
|
-
/* c8 ignore start @preserve -- defensive guard, not reachable through
|
|
898
|
-
the public API. enumerateValidSplits's only caller (parse.ts, both
|
|
899
|
-
call sites) passes run.tokens straight from
|
|
900
|
-
pattern.ambiguousRuns, which parsePattern.ts only ever populates
|
|
901
|
-
with tokens already checked against UNPADDED_NUMERIC_TOKENS — the
|
|
902
|
-
exact same key set as UNPADDED_NUMERIC_RANGES. There's no path
|
|
903
|
-
where a token reaches here without having already passed that
|
|
904
|
-
check. */
|
|
905
|
-
if (!ranges) {
|
|
906
|
-
throw new Error(`temporal-fmt: internal error \u2014 "${token}" is not an unpadded numeric token`);
|
|
907
|
-
}
|
|
908
|
-
/* c8 ignore stop @preserve */
|
|
909
|
-
const results = [];
|
|
910
|
-
for (const { digits: width, min, max } of ranges) {
|
|
911
|
-
if (offset + width > digits.length) continue;
|
|
912
|
-
const piece = digits.slice(offset, offset + width);
|
|
913
|
-
if (width === 2 && piece[0] === "0") continue;
|
|
914
|
-
const value = Number(piece);
|
|
915
|
-
if (value < min || value > max) continue;
|
|
916
|
-
for (const restSplit of solve(tokenIndex + 1, offset + width)) {
|
|
917
|
-
results.push([value, ...restSplit]);
|
|
918
|
-
if (results.length === 2) break;
|
|
919
|
-
}
|
|
920
|
-
if (results.length === 2) break;
|
|
921
|
-
}
|
|
922
|
-
memo.set(key, results);
|
|
923
|
-
return results;
|
|
924
|
-
}
|
|
925
|
-
return solve(0, 0);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
// src/parsePattern.ts
|
|
929
|
-
function escapeRegExp2(literal) {
|
|
930
|
-
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
931
|
-
}
|
|
932
|
-
function buildCapturingPattern(pieces, locale) {
|
|
933
|
-
const groups = [];
|
|
934
|
-
const ambiguousRuns = [];
|
|
935
|
-
let source = "";
|
|
936
|
-
let i = 0;
|
|
937
|
-
let currentRun = { groupNames: [], tokens: [] };
|
|
938
|
-
const flushRun = () => {
|
|
939
|
-
if (currentRun.tokens.length >= 2) {
|
|
940
|
-
ambiguousRuns.push(currentRun);
|
|
941
|
-
}
|
|
942
|
-
currentRun = { groupNames: [], tokens: [] };
|
|
943
|
-
};
|
|
944
|
-
for (const [idx, piece] of pieces.entries()) {
|
|
945
|
-
if (piece.kind === "literal") {
|
|
946
|
-
source += escapeRegExp2(piece.value);
|
|
947
|
-
flushRun();
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
const name = `g${i++}`;
|
|
951
|
-
groups.push({ name, token: piece.value });
|
|
952
|
-
const nextPiece = pieces[idx + 1];
|
|
953
|
-
const nextToken = nextPiece?.kind === "token" ? nextPiece.value : void 0;
|
|
954
|
-
source += `(?<${name}>${tokenFragment(piece.value, locale, nextToken)})`;
|
|
955
|
-
if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {
|
|
956
|
-
currentRun.groupNames.push(name);
|
|
957
|
-
currentRun.tokens.push(piece.value);
|
|
958
|
-
} else {
|
|
959
|
-
flushRun();
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
flushRun();
|
|
963
|
-
return { regex: new RegExp(`^(?:${source})$`, "ud"), groups, ambiguousRuns };
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
// src/constants.ts
|
|
967
|
-
var MAX_FORMAT_LENGTH = 1e3;
|
|
968
|
-
var MAX_INPUT_LENGTH = 1e5;
|
|
969
|
-
|
|
970
|
-
// src/numbering.ts
|
|
971
|
-
var SUPPORTED_NUMBERING_SYSTEMS = /* @__PURE__ */ new Set([
|
|
972
|
-
"latn",
|
|
973
|
-
"arab",
|
|
974
|
-
"deva",
|
|
975
|
-
"beng",
|
|
976
|
-
"guru",
|
|
977
|
-
"gujr",
|
|
978
|
-
"orya",
|
|
979
|
-
"tamldec",
|
|
980
|
-
"telu",
|
|
981
|
-
"knda",
|
|
982
|
-
"mlym",
|
|
983
|
-
"fullwide",
|
|
984
|
-
"hanidec"
|
|
985
|
-
]);
|
|
986
|
-
var digitMapCache = /* @__PURE__ */ new Map();
|
|
987
|
-
function getDigitMap(system) {
|
|
988
|
-
let map = digitMapCache.get(system);
|
|
989
|
-
if (map) return map;
|
|
990
|
-
/* c8 ignore start @preserve -- dead by construction, not just
|
|
991
|
-
untested: both callers (convertDigits, convertDigitsToAscii) already
|
|
992
|
-
return early on system === 'latn' before calling getDigitMap at all,
|
|
993
|
-
so this function is never invoked with 'latn'. Kept as a defensive
|
|
994
|
-
fallback rather than trusting that stays true for any future
|
|
995
|
-
caller. */
|
|
996
|
-
if (system === "latn") {
|
|
997
|
-
map = {};
|
|
998
|
-
for (let i = 0; i < 10; i++) map[String(i)] = String(i);
|
|
999
|
-
} else {
|
|
1000
|
-
/* c8 ignore stop @preserve */
|
|
1001
|
-
const fmt = new Intl.NumberFormat("en-US-u-nu-" + system, { useGrouping: false });
|
|
1002
|
-
map = {};
|
|
1003
|
-
for (let i = 0; i < 10; i++) {
|
|
1004
|
-
map[String(i)] = fmt.format(i);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
digitMapCache.set(system, map);
|
|
1008
|
-
return map;
|
|
1009
|
-
}
|
|
1010
|
-
function convertDigitsToAscii(s, system) {
|
|
1011
|
-
if (system === "latn") return s;
|
|
1012
|
-
if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {
|
|
1013
|
-
throw new InvalidLocaleError({ actual: system, reason: `numbering system "${system}" is not supported.` });
|
|
1014
|
-
}
|
|
1015
|
-
const map = getDigitMap(system);
|
|
1016
|
-
const reverse = {};
|
|
1017
|
-
for (const k of Object.keys(map)) reverse[map[k]] = k;
|
|
1018
|
-
let result = "";
|
|
1019
|
-
for (const ch of s) {
|
|
1020
|
-
result += reverse[ch] ?? ch;
|
|
1021
|
-
}
|
|
1022
|
-
return result;
|
|
1023
|
-
}
|
|
1024
|
-
function applyParseNumbering(s, options) {
|
|
1025
|
-
const system = options.parseNumberingSystem ?? "latn";
|
|
1026
|
-
if (system === "latn") return s;
|
|
1027
|
-
return convertDigitsToAscii(s, system);
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// src/parse.ts
|
|
1031
|
-
var patternCache = /* @__PURE__ */ new Map();
|
|
1032
|
-
var MAX_CACHE_SIZE2 = 500;
|
|
1033
|
-
function getPattern(formatStr, locale) {
|
|
1034
|
-
const key = JSON.stringify([canonicalCacheKey(locale), formatStr]);
|
|
1035
|
-
let pattern = patternCache.get(key);
|
|
1036
|
-
if (pattern) {
|
|
1037
|
-
return pattern;
|
|
1038
|
-
}
|
|
1039
|
-
if (patternCache.size >= MAX_CACHE_SIZE2) {
|
|
1040
|
-
const oldestKey = patternCache.keys().next().value;
|
|
1041
|
-
if (oldestKey !== void 0) patternCache.delete(oldestKey);
|
|
1042
|
-
}
|
|
1043
|
-
pattern = buildCapturingPattern(tokenize(formatStr), locale);
|
|
1044
|
-
patternCache.set(key, pattern);
|
|
1045
|
-
return pattern;
|
|
1046
|
-
}
|
|
1047
|
-
var calendarCache = /* @__PURE__ */ new Map();
|
|
1048
|
-
var MAX_CALENDAR_CACHE_SIZE = 500;
|
|
1049
|
-
function resolveCalendar(locale) {
|
|
1050
|
-
const canonicalLocale = new Intl.Locale(locale).toString().toLowerCase();
|
|
1051
|
-
if (calendarCache.has(canonicalLocale)) {
|
|
1052
|
-
return calendarCache.get(canonicalLocale);
|
|
1053
|
-
}
|
|
1054
|
-
if (calendarCache.size >= MAX_CALENDAR_CACHE_SIZE) {
|
|
1055
|
-
const oldestKey = calendarCache.keys().next().value;
|
|
1056
|
-
if (oldestKey !== void 0) calendarCache.delete(oldestKey);
|
|
1057
|
-
}
|
|
1058
|
-
let calendar;
|
|
1059
|
-
const parts = canonicalLocale.split("-");
|
|
1060
|
-
const extensionIndex = parts.indexOf("u");
|
|
1061
|
-
const calendarKeyIndex = extensionIndex === -1 ? -1 : parts.indexOf("ca", extensionIndex + 1);
|
|
1062
|
-
if (calendarKeyIndex !== -1 && calendarKeyIndex + 1 < parts.length) {
|
|
1063
|
-
const resolved = new Intl.DateTimeFormat(canonicalLocale).resolvedOptions().calendar;
|
|
1064
|
-
calendar = resolved === "gregory" ? void 0 : resolved;
|
|
1065
|
-
}
|
|
1066
|
-
calendarCache.set(canonicalLocale, calendar);
|
|
1067
|
-
return calendar;
|
|
1068
|
-
}
|
|
1069
|
-
function parseOffsetString(raw, token) {
|
|
1070
|
-
if (raw === "Z") {
|
|
1071
|
-
/* c8 ignore start @preserve -- unreachable: lowercase tokens' regex
|
|
1072
|
-
(OFFSET_SHAPES in pattern.ts) has no "Z" alternative at all, so
|
|
1073
|
-
raw === 'Z' can only ever be reached when token is one of the
|
|
1074
|
-
uppercase variants (X/XX/XXX). A lowercase token can't even
|
|
1075
|
-
capture "Z" as `raw` in the first place. */
|
|
1076
|
-
if (token === "x" || token === "xx" || token === "xxx") {
|
|
1077
|
-
throw new Error(
|
|
1078
|
-
`temporal-fmt: offset token "${token}" 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.`
|
|
1079
|
-
);
|
|
1080
|
-
}
|
|
1081
|
-
/* c8 ignore stop @preserve */
|
|
1082
|
-
return "+00:00";
|
|
1083
|
-
}
|
|
1084
|
-
const sign = raw[0];
|
|
1085
|
-
/* c8 ignore start @preserve -- unreachable: raw is a regex-captured
|
|
1086
|
-
group from an offset token, and every OFFSET_SHAPES pattern
|
|
1087
|
-
(pattern.ts) is anchored to either "Z" or a leading [+-]. raw's
|
|
1088
|
-
first character can never be anything else by the time it reaches
|
|
1089
|
-
this function. */
|
|
1090
|
-
if (sign !== "+" && sign !== "-") {
|
|
1091
|
-
throw new Error(`temporal-fmt: offset "${raw}" for token "${token}" doesn't start with "+", "-", or "Z".`);
|
|
1092
|
-
}
|
|
1093
|
-
/* c8 ignore stop @preserve */
|
|
1094
|
-
const body = raw.slice(1);
|
|
1095
|
-
let hoursStr;
|
|
1096
|
-
let minutesStr;
|
|
1097
|
-
if (body.length === 2) {
|
|
1098
|
-
/* c8 ignore start @preserve -- unreachable: each offset token's own
|
|
1099
|
-
regex shape in OFFSET_SHAPES (pattern.ts) already gates which
|
|
1100
|
-
body shapes it can capture. Only X and x ever match a 2-digit
|
|
1101
|
-
body — XX/xx/XXX/xxx's regexes can't produce one — so this
|
|
1102
|
-
mismatch can never actually fire through parse(). */
|
|
1103
|
-
if (token !== "X" && token !== "x") {
|
|
1104
|
-
throw new Error(
|
|
1105
|
-
`temporal-fmt: offset token "${token}" can't match "${raw}" \u2014 it requires minutes, but "${raw}" has none.`
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
/* c8 ignore stop @preserve */
|
|
1109
|
-
hoursStr = body;
|
|
1110
|
-
minutesStr = "00";
|
|
1111
|
-
} else if (body.length === 4) {
|
|
1112
|
-
/* c8 ignore start @preserve -- unreachable, same reason as the
|
|
1113
|
-
2-digit case above: XXX/xxx's regex requires a colon, so it can
|
|
1114
|
-
never capture a 4-digit no-colon body in the first place. */
|
|
1115
|
-
if (token === "XXX" || token === "xxx") {
|
|
1116
|
-
throw new Error(
|
|
1117
|
-
`temporal-fmt: offset token "${token}" can't match "${raw}" \u2014 it requires a colon between hours and minutes (e.g. "${sign}${body.slice(0, 2)}:${body.slice(2)}").`
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
/* c8 ignore stop @preserve */
|
|
1121
|
-
hoursStr = body.slice(0, 2);
|
|
1122
|
-
minutesStr = body.slice(2, 4);
|
|
1123
|
-
} else if (body.length === 5 && body[2] === ":") {
|
|
1124
|
-
/* c8 ignore start @preserve -- unreachable, same reason again: only
|
|
1125
|
-
XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never
|
|
1126
|
-
capture one. */
|
|
1127
|
-
if (token !== "XXX" && token !== "xxx") {
|
|
1128
|
-
throw new Error(
|
|
1129
|
-
`temporal-fmt: offset token "${token}" can't match "${raw}" \u2014 it doesn't use a colon (use "${sign}${body.slice(0, 2)}${body.slice(3)}" instead).`
|
|
1130
|
-
);
|
|
1131
|
-
}
|
|
1132
|
-
/* c8 ignore stop @preserve */
|
|
1133
|
-
hoursStr = body.slice(0, 2);
|
|
1134
|
-
minutesStr = body.slice(3, 5);
|
|
1135
|
-
/* c8 ignore start @preserve -- unreachable: every offset token's
|
|
1136
|
-
regex only ever produces a body of length 2, length 4, or length 5
|
|
1137
|
-
with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no
|
|
1138
|
-
shape falls outside those three cases, so this else arm can't be
|
|
1139
|
-
taken through parse(). Kept as an exhaustiveness fallback so
|
|
1140
|
-
hoursStr/minutesStr are assigned on every path TypeScript can see. */
|
|
1141
|
-
} else {
|
|
1142
|
-
throw new Error(`temporal-fmt: offset "${raw}" doesn't match the shape token "${token}" accepts.`);
|
|
1143
|
-
}
|
|
1144
|
-
/* c8 ignore stop @preserve */
|
|
1145
|
-
const hours = Number(hoursStr);
|
|
1146
|
-
const minutes = Number(minutesStr);
|
|
1147
|
-
if (hours > 14) {
|
|
1148
|
-
throw new InvalidOffsetError({
|
|
1149
|
-
actual: raw,
|
|
1150
|
-
message: `temporal-fmt: offset hours ${hours} in "${raw}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
if (minutes > 59) {
|
|
1154
|
-
throw new InvalidOffsetError({
|
|
1155
|
-
actual: raw,
|
|
1156
|
-
message: `temporal-fmt: offset minutes ${minutes} in "${raw}" out of range (max 59).`
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
if (sign === "+" && hours === 14 && minutes !== 0) {
|
|
1160
|
-
throw new InvalidOffsetError({
|
|
1161
|
-
actual: raw,
|
|
1162
|
-
message: `temporal-fmt: offset "${raw}" exceeds the maximum supported UTC offset of +14:00.`
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
if (sign === "-" && hours === 12 && minutes !== 0) {
|
|
1166
|
-
throw new InvalidOffsetError({
|
|
1167
|
-
actual: raw,
|
|
1168
|
-
message: `temporal-fmt: offset "${raw}" exceeds the maximum supported negative UTC offset of -12:00.`
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
return `${sign}${hoursStr}:${minutesStr}`;
|
|
1172
|
-
}
|
|
1173
|
-
function assignField(fields, key, value) {
|
|
1174
|
-
fields[key] = value;
|
|
1175
|
-
}
|
|
1176
|
-
function applyGroup(fields, token, raw, locale, formatStr) {
|
|
1177
|
-
const vocab = getLocaleVocab(locale);
|
|
1178
|
-
switch (token) {
|
|
1179
|
-
case "yyyy":
|
|
1180
|
-
assignField(fields, "year", Number(raw));
|
|
1181
|
-
break;
|
|
1182
|
-
case "yy":
|
|
1183
|
-
assignField(fields, "twoDigitYear", Number(raw));
|
|
1184
|
-
break;
|
|
1185
|
-
case "MM":
|
|
1186
|
-
case "M":
|
|
1187
|
-
assignField(fields, "month", Number(raw));
|
|
1188
|
-
break;
|
|
1189
|
-
case "MMMM":
|
|
1190
|
-
assignField(fields, "month", vocab.monthLong.indexOf(raw) + 1);
|
|
1191
|
-
break;
|
|
1192
|
-
case "MMM":
|
|
1193
|
-
assignField(fields, "month", vocab.monthShort.indexOf(raw) + 1);
|
|
1194
|
-
break;
|
|
1195
|
-
case "dd":
|
|
1196
|
-
case "d":
|
|
1197
|
-
assignField(fields, "day", Number(raw));
|
|
1198
|
-
break;
|
|
1199
|
-
case "EEEE":
|
|
1200
|
-
assignField(fields, "weekdayRaw", raw);
|
|
1201
|
-
assignField(fields, "weekdayExpected", vocab.weekdayLong.indexOf(raw) + 1);
|
|
1202
|
-
break;
|
|
1203
|
-
case "EEE":
|
|
1204
|
-
assignField(fields, "weekdayRaw", raw);
|
|
1205
|
-
assignField(fields, "weekdayExpected", vocab.weekdayShort.indexOf(raw) + 1);
|
|
1206
|
-
break;
|
|
1207
|
-
case "HH":
|
|
1208
|
-
case "H":
|
|
1209
|
-
assignField(fields, "hour", Number(raw));
|
|
1210
|
-
break;
|
|
1211
|
-
case "hh":
|
|
1212
|
-
case "h":
|
|
1213
|
-
assignField(fields, "hour12", Number(raw));
|
|
1214
|
-
break;
|
|
1215
|
-
case "mm":
|
|
1216
|
-
case "m":
|
|
1217
|
-
assignField(fields, "minute", Number(raw));
|
|
1218
|
-
break;
|
|
1219
|
-
case "ss":
|
|
1220
|
-
case "s":
|
|
1221
|
-
assignField(fields, "second", Number(raw));
|
|
1222
|
-
break;
|
|
1223
|
-
case "S":
|
|
1224
|
-
case "SS":
|
|
1225
|
-
case "SSS":
|
|
1226
|
-
case "SSSS":
|
|
1227
|
-
case "SSSSS":
|
|
1228
|
-
case "SSSSSS":
|
|
1229
|
-
case "SSSSSSS":
|
|
1230
|
-
case "SSSSSSSS":
|
|
1231
|
-
case "SSSSSSSSS": {
|
|
1232
|
-
const nanoOfSecond = Number(raw.padEnd(9, "0"));
|
|
1233
|
-
assignField(fields, "millisecond", Math.floor(nanoOfSecond / 1e6));
|
|
1234
|
-
assignField(fields, "microsecond", Math.floor(nanoOfSecond / 1e3) % 1e3);
|
|
1235
|
-
assignField(fields, "nanosecond", nanoOfSecond % 1e3);
|
|
1236
|
-
break;
|
|
1237
|
-
}
|
|
1238
|
-
case "a": {
|
|
1239
|
-
const periodIndex = vocab.dayPeriod.findIndex((p) => p.toLowerCase() === raw.toLowerCase());
|
|
1240
|
-
/* c8 ignore start @preserve -- unreachable: the 'a' token's regex
|
|
1241
|
-
fragment (pattern.ts's alternation() over vocab.dayPeriod) can
|
|
1242
|
-
only ever capture a case-insensitive match of one of
|
|
1243
|
-
vocab.dayPeriod's own entries. Both the regex and this lookup
|
|
1244
|
-
derive their vocab from the same `locale` via getLocaleVocab(),
|
|
1245
|
-
so periodIndex can't come back negative through parse(). */
|
|
1246
|
-
if (periodIndex < 0) throw new Error(`temporal-fmt: unknown day period "${raw}" for locale "${locale}".`);
|
|
1247
|
-
/* c8 ignore stop @preserve */
|
|
1248
|
-
assignField(fields, "dayPeriodRaw", raw);
|
|
1249
|
-
assignField(fields, "isPM", periodIndex === 1);
|
|
1250
|
-
break;
|
|
1251
|
-
}
|
|
1252
|
-
case "zzz":
|
|
1253
|
-
assignField(fields, "timeZoneId", raw);
|
|
1254
|
-
break;
|
|
1255
|
-
case "X":
|
|
1256
|
-
case "XX":
|
|
1257
|
-
case "XXX":
|
|
1258
|
-
case "x":
|
|
1259
|
-
case "xx":
|
|
1260
|
-
case "xxx":
|
|
1261
|
-
assignField(fields, "offsetString", parseOffsetString(raw, token));
|
|
1262
|
-
break;
|
|
1263
|
-
case "Q":
|
|
1264
|
-
assignField(fields, "quarter", Number(raw));
|
|
1265
|
-
break;
|
|
1266
|
-
case "QQQ":
|
|
1267
|
-
assignField(fields, "quarter", Number(raw.slice(1)));
|
|
1268
|
-
break;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
function pickLenientSplit(splits, tokens) {
|
|
1272
|
-
const dayIndex = tokens.indexOf("d");
|
|
1273
|
-
if (dayIndex !== -1) {
|
|
1274
|
-
const smallDaySplits = splits.filter((s) => s[dayIndex] <= 12);
|
|
1275
|
-
if (smallDaySplits.length > 0) {
|
|
1276
|
-
return smallDaySplits[0];
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
return splits[0];
|
|
1280
|
-
}
|
|
1281
|
-
function resolveYear(fields) {
|
|
1282
|
-
if (fields.year !== void 0 && fields.twoDigitYear !== void 0) {
|
|
1283
|
-
throw new FormatSyntaxError({
|
|
1284
|
-
message: 'temporal-fmt: format string mixes "yyyy" and "yy" year representations.'
|
|
1285
|
-
});
|
|
1286
|
-
}
|
|
1287
|
-
if (fields.year !== void 0) return fields.year;
|
|
1288
|
-
if (fields.twoDigitYear !== void 0) {
|
|
1289
|
-
return fields.twoDigitYear <= 68 ? 2e3 + fields.twoDigitYear : 1900 + fields.twoDigitYear;
|
|
1290
|
-
}
|
|
1291
|
-
return void 0;
|
|
1292
|
-
}
|
|
1293
|
-
function resolveHour(fields, formatStr, locale) {
|
|
1294
|
-
if (fields.hour !== void 0 && fields.hour12 !== void 0) {
|
|
1295
|
-
throw new FormatSyntaxError({
|
|
1296
|
-
format: formatStr,
|
|
1297
|
-
message: `temporal-fmt: format string "${formatStr}" mixes a 24-hour token ("HH"/"H") with a 12-hour token ("hh"/"h").`
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
if (fields.hour !== void 0) {
|
|
1301
|
-
if (fields.dayPeriodRaw !== void 0) {
|
|
1302
|
-
const vocab = getLocaleVocab(locale);
|
|
1303
|
-
const expected = fields.hour < 12 ? vocab.dayPeriod[0] : vocab.dayPeriod[1];
|
|
1304
|
-
if (fields.dayPeriodRaw.toLowerCase() !== expected?.toLowerCase()) {
|
|
1305
|
-
throw new FormatSyntaxError({
|
|
1306
|
-
format: formatStr,
|
|
1307
|
-
message: `temporal-fmt: format string "${formatStr}" contains a day period that contradicts the 24-hour value.`
|
|
1308
|
-
});
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
return fields.hour;
|
|
1312
|
-
}
|
|
1313
|
-
if (fields.hour12 !== void 0) {
|
|
1314
|
-
if (fields.isPM === void 0) {
|
|
1315
|
-
throw new FormatSyntaxError({
|
|
1316
|
-
format: formatStr,
|
|
1317
|
-
message: `temporal-fmt: format string "${formatStr}" uses a 12-hour token ("hh"/"h") without an "a" token, so parse() can't tell AM from PM.`
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return fields.hour12 % 12 + (fields.isPM ? 12 : 0);
|
|
1321
|
-
}
|
|
1322
|
-
return void 0;
|
|
1323
|
-
}
|
|
1324
|
-
function parse(formatStr, input, options = {}) {
|
|
1325
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
1326
|
-
throw new FormatSyntaxError({
|
|
1327
|
-
format: formatStr,
|
|
1328
|
-
message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
if (input.length > MAX_INPUT_LENGTH) {
|
|
1332
|
-
throw new FormatSyntaxError({
|
|
1333
|
-
input,
|
|
1334
|
-
message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
|
|
1335
|
-
});
|
|
1336
|
-
}
|
|
1337
|
-
if (options.parseNumberingSystem) {
|
|
1338
|
-
input = applyParseNumbering(input, options);
|
|
1339
|
-
}
|
|
1340
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
1341
|
-
const calendar = resolveCalendar(locale);
|
|
1342
|
-
const pattern = getPattern(formatStr, locale);
|
|
1343
|
-
const match = pattern.regex.exec(input);
|
|
1344
|
-
if (!match) {
|
|
1345
|
-
throw new ParseMismatchError({
|
|
1346
|
-
input,
|
|
1347
|
-
format: formatStr,
|
|
1348
|
-
// reason set explicitly (not just message): parse.test.js's
|
|
1349
|
-
// "safeParse: failure returns { ok: false, error } with a
|
|
1350
|
-
// TemporalFmtError" asserts result.error.reason carries the
|
|
1351
|
-
// underlying detail text, matching what wrapUntypedError used to
|
|
1352
|
-
// populate when this message reached it as a caught plain Error.
|
|
1353
|
-
reason: "no valid pattern matches the format string and input shape",
|
|
1354
|
-
message: `temporal-fmt: no valid pattern matches the format string and input shape`
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
if (pattern.groups.length === 0) {
|
|
1358
|
-
throw new ParseMismatchError({
|
|
1359
|
-
format: formatStr,
|
|
1360
|
-
// reason set explicitly: errors.test.js's "format string with no
|
|
1361
|
-
// tokens at all falls through to ParseMismatchError" asserts
|
|
1362
|
-
// error.reason matches /has no tokens/.
|
|
1363
|
-
reason: `format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`,
|
|
1364
|
-
message: `temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`
|
|
1365
|
-
});
|
|
1366
|
-
}
|
|
1367
|
-
for (const { name, token } of pattern.groups) {
|
|
1368
|
-
if (token === "zzz" && !isValidTimeZone(match.groups[name])) {
|
|
1369
|
-
throw new InvalidTimeZoneError({
|
|
1370
|
-
input,
|
|
1371
|
-
format: formatStr,
|
|
1372
|
-
actual: match.groups[name],
|
|
1373
|
-
reason: "not a recognized IANA time zone identifier"
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
const runPicks = [];
|
|
1378
|
-
for (const run of pattern.ambiguousRuns) {
|
|
1379
|
-
const runDigits = run.groupNames.map((name) => match.groups[name]).join("");
|
|
1380
|
-
const splits = enumerateValidSplits(runDigits, run.tokens);
|
|
1381
|
-
if (splits.length > 1) {
|
|
1382
|
-
if (!options.lenient) {
|
|
1383
|
-
throw new AmbiguousInputError({
|
|
1384
|
-
input,
|
|
1385
|
-
format: formatStr,
|
|
1386
|
-
message: `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
runPicks.push({ groupNames: run.groupNames, values: pickLenientSplit(splits, run.tokens) });
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
const fields = {};
|
|
1393
|
-
const lenientValues = /* @__PURE__ */ new Map();
|
|
1394
|
-
for (const { groupNames, values } of runPicks) {
|
|
1395
|
-
groupNames.forEach((name, i) => lenientValues.set(name, String(values[i])));
|
|
1396
|
-
}
|
|
1397
|
-
for (const { name, token } of pattern.groups) {
|
|
1398
|
-
const raw = lenientValues.get(name) ?? match.groups[name];
|
|
1399
|
-
applyGroup(fields, token, raw, locale, formatStr);
|
|
1400
|
-
}
|
|
1401
|
-
const year = resolveYear(fields);
|
|
1402
|
-
const hour = resolveHour(fields, formatStr, locale);
|
|
1403
|
-
const { month, day, minute, second, millisecond, microsecond, nanosecond, timeZoneId, offsetString, weekdayExpected, weekdayRaw, quarter } = fields;
|
|
1404
|
-
const hasAnyDatePart = year !== void 0 || month !== void 0 || day !== void 0;
|
|
1405
|
-
const hasFullDate = year !== void 0 && month !== void 0 && day !== void 0;
|
|
1406
|
-
if (hasAnyDatePart && !hasFullDate) {
|
|
1407
|
-
throw new InvalidDateError({
|
|
1408
|
-
format: formatStr,
|
|
1409
|
-
message: `temporal-fmt: format string "${formatStr}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
const hasTime = hour !== void 0 || minute !== void 0 || second !== void 0 || millisecond !== void 0;
|
|
1413
|
-
if (timeZoneId !== void 0 && !(hasFullDate && hasTime)) {
|
|
1414
|
-
throw new FormatSyntaxError({
|
|
1415
|
-
format: formatStr,
|
|
1416
|
-
message: `temporal-fmt: format string "${formatStr}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
if (offsetString !== void 0 && !(hasFullDate && hasTime)) {
|
|
1420
|
-
throw new FormatSyntaxError({
|
|
1421
|
-
format: formatStr,
|
|
1422
|
-
message: `temporal-fmt: format string "${formatStr}" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time to build a ZonedDateTime.`
|
|
1423
|
-
});
|
|
1424
|
-
}
|
|
1425
|
-
if (weekdayExpected !== void 0 && !hasFullDate) {
|
|
1426
|
-
throw new FormatSyntaxError({
|
|
1427
|
-
format: formatStr,
|
|
1428
|
-
message: `temporal-fmt: format string "${formatStr}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`
|
|
1429
|
-
});
|
|
1430
|
-
}
|
|
1431
|
-
if (!hasFullDate && !hasTime) {
|
|
1432
|
-
throw new FormatSyntaxError({
|
|
1433
|
-
format: formatStr,
|
|
1434
|
-
message: `temporal-fmt: format string "${formatStr}" has no date or time tokens to parse.`
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
const temporal = getTemporal();
|
|
1438
|
-
const timeFields = {
|
|
1439
|
-
hour: hour ?? 0,
|
|
1440
|
-
minute: minute ?? 0,
|
|
1441
|
-
second: second ?? 0,
|
|
1442
|
-
millisecond: millisecond ?? 0,
|
|
1443
|
-
microsecond: microsecond ?? 0,
|
|
1444
|
-
nanosecond: nanosecond ?? 0
|
|
1445
|
-
};
|
|
1446
|
-
const calendarField = calendar ? { calendar } : {};
|
|
1447
|
-
const reject = { overflow: "reject" };
|
|
1448
|
-
let result;
|
|
1449
|
-
try {
|
|
1450
|
-
if (timeZoneId !== void 0) {
|
|
1451
|
-
const zoneOptions = { overflow: "reject", offset: "prefer" };
|
|
1452
|
-
result = temporal.ZonedDateTime.from(
|
|
1453
|
-
{
|
|
1454
|
-
year,
|
|
1455
|
-
month,
|
|
1456
|
-
day,
|
|
1457
|
-
...timeFields,
|
|
1458
|
-
...calendarField,
|
|
1459
|
-
timeZone: timeZoneId,
|
|
1460
|
-
...offsetString !== void 0 ? { offset: offsetString } : {}
|
|
1461
|
-
},
|
|
1462
|
-
zoneOptions
|
|
1463
|
-
);
|
|
1464
|
-
if (offsetString !== void 0) {
|
|
1465
|
-
const zdt = result;
|
|
1466
|
-
const wallClockShifted = zdt.hour !== timeFields.hour || zdt.minute !== timeFields.minute || zdt.second !== timeFields.second;
|
|
1467
|
-
if (wallClockShifted) {
|
|
1468
|
-
throw new InvalidDateError({
|
|
1469
|
-
input,
|
|
1470
|
-
format: formatStr,
|
|
1471
|
-
message: `"${timeZoneId}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
const actualOffset = zdt.offset;
|
|
1475
|
-
if (actualOffset !== offsetString) {
|
|
1476
|
-
throw new ParseMismatchError({
|
|
1477
|
-
input,
|
|
1478
|
-
format: formatStr,
|
|
1479
|
-
message: `has both a "zzz" zone (${timeZoneId}) and an offset token (${offsetString}), but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`
|
|
1480
|
-
});
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
} else if (offsetString !== void 0) {
|
|
1484
|
-
result = temporal.ZonedDateTime.from({ year, month, day, ...timeFields, ...calendarField, timeZone: offsetString }, reject);
|
|
1485
|
-
} else if (hasFullDate && hasTime) {
|
|
1486
|
-
result = temporal.PlainDateTime.from({ year, month, day, ...timeFields, ...calendarField }, reject);
|
|
1487
|
-
} else if (hasFullDate) {
|
|
1488
|
-
result = temporal.PlainDate.from({ year, month, day, ...calendarField }, reject);
|
|
1489
|
-
} else {
|
|
1490
|
-
result = temporal.PlainTime.from(timeFields, reject);
|
|
1491
|
-
}
|
|
1492
|
-
} catch (err) {
|
|
1493
|
-
throw new InvalidDateError({
|
|
1494
|
-
input,
|
|
1495
|
-
format: formatStr,
|
|
1496
|
-
message: `temporal-fmt: "${input}" doesn't describe a valid date/time for format "${formatStr}": ${err.message}`
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
if (weekdayExpected !== void 0) {
|
|
1500
|
-
const actual = result.dayOfWeek;
|
|
1501
|
-
if (actual !== weekdayExpected) {
|
|
1502
|
-
const vocab = getLocaleVocab(locale);
|
|
1503
|
-
throw new InvalidDateError({
|
|
1504
|
-
input,
|
|
1505
|
-
format: formatStr,
|
|
1506
|
-
message: `temporal-fmt: "${weekdayRaw}" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) for the parsed date.`
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
if (quarter !== void 0 && month !== void 0) {
|
|
1511
|
-
const expectedQuarter = Math.ceil(month / 3);
|
|
1512
|
-
if (quarter !== expectedQuarter) {
|
|
1513
|
-
throw new InvalidDateError({
|
|
1514
|
-
format: formatStr,
|
|
1515
|
-
message: `temporal-fmt: format string "${formatStr}" contains a quarter token (Q/QQQ) whose value (Q${quarter}) disagrees with the parsed month's actual quarter \u2014 month ${month} is in Q${expectedQuarter}.`
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
return result;
|
|
1520
|
-
}
|
|
1521
|
-
function safeParse(formatStr, input, options = {}) {
|
|
1522
|
-
try {
|
|
1523
|
-
return { ok: true, value: parse(formatStr, input, options) };
|
|
1524
|
-
} catch (err) {
|
|
1525
|
-
/* c8 ignore start @preserve */
|
|
1526
|
-
if (err instanceof TemporalFmtError) {
|
|
1527
|
-
return { ok: false, error: err };
|
|
1528
|
-
}
|
|
1529
|
-
return { ok: false, error: wrapUntypedError(err, { input, format: formatStr }) };
|
|
1530
|
-
}
|
|
1531
|
-
/* c8 ignore stop @preserve */
|
|
1532
|
-
}
|
|
1533
|
-
function tryParse(formatStr, input, options = {}) {
|
|
1534
|
-
try {
|
|
1535
|
-
return parse(formatStr, input, options);
|
|
1536
|
-
} catch {
|
|
1537
|
-
return void 0;
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
function parseToParts(formatStr, input, options = {}) {
|
|
1541
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
1542
|
-
throw new FormatSyntaxError({
|
|
1543
|
-
format: formatStr,
|
|
1544
|
-
message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
1545
|
-
});
|
|
1546
|
-
}
|
|
1547
|
-
if (input.length > MAX_INPUT_LENGTH) {
|
|
1548
|
-
throw new FormatSyntaxError({
|
|
1549
|
-
input,
|
|
1550
|
-
message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`
|
|
1551
|
-
});
|
|
1552
|
-
}
|
|
1553
|
-
if (options.parseNumberingSystem) {
|
|
1554
|
-
input = applyParseNumbering(input, options);
|
|
1555
|
-
}
|
|
1556
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
1557
|
-
const pattern = getPattern(formatStr, locale);
|
|
1558
|
-
const match = pattern.regex.exec(input);
|
|
1559
|
-
if (!match) {
|
|
1560
|
-
throw new ParseMismatchError({
|
|
1561
|
-
input,
|
|
1562
|
-
format: formatStr,
|
|
1563
|
-
// reason set explicitly (not just message): parse.test.js's
|
|
1564
|
-
// "safeParse: failure returns { ok: false, error } with a
|
|
1565
|
-
// TemporalFmtError" asserts result.error.reason carries the
|
|
1566
|
-
// underlying detail text, matching what wrapUntypedError used to
|
|
1567
|
-
// populate when this message reached it as a caught plain Error.
|
|
1568
|
-
reason: "no valid pattern matches the format string and input shape",
|
|
1569
|
-
message: `temporal-fmt: no valid pattern matches the format string and input shape`
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
if (pattern.groups.length === 0) {
|
|
1573
|
-
throw new ParseMismatchError({
|
|
1574
|
-
format: formatStr,
|
|
1575
|
-
// reason set explicitly: errors.test.js's "format string with no
|
|
1576
|
-
// tokens at all falls through to ParseMismatchError" asserts
|
|
1577
|
-
// error.reason matches /has no tokens/.
|
|
1578
|
-
reason: `format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`,
|
|
1579
|
-
message: `temporal-fmt: format string "${formatStr}" has no tokens \u2014 nothing to parse into a value.`
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
for (const { name, token } of pattern.groups) {
|
|
1583
|
-
if (token === "zzz" && !isValidTimeZone(match.groups[name])) {
|
|
1584
|
-
throw new InvalidTimeZoneError({
|
|
1585
|
-
input,
|
|
1586
|
-
format: formatStr,
|
|
1587
|
-
actual: match.groups[name],
|
|
1588
|
-
reason: "not a recognized IANA time zone identifier"
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
const runPicks = [];
|
|
1593
|
-
for (const run of pattern.ambiguousRuns) {
|
|
1594
|
-
const runDigits = run.groupNames.map((name) => match.groups[name]).join("");
|
|
1595
|
-
const splits = enumerateValidSplits(runDigits, run.tokens);
|
|
1596
|
-
if (splits.length > 1) {
|
|
1597
|
-
if (!options.lenient) {
|
|
1598
|
-
throw new AmbiguousInputError({
|
|
1599
|
-
input,
|
|
1600
|
-
format: formatStr,
|
|
1601
|
-
message: `temporal-fmt: "${runDigits}" in format string "${formatStr}" is ambiguous \u2014 ${splits.length} different ways to read tokens "${run.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[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.`
|
|
1602
|
-
});
|
|
1603
|
-
}
|
|
1604
|
-
runPicks.push({ groupNames: run.groupNames, values: pickLenientSplit(splits, run.tokens) });
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
const lenientValues = /* @__PURE__ */ new Map();
|
|
1608
|
-
for (const { groupNames, values } of runPicks) {
|
|
1609
|
-
groupNames.forEach((name, i) => lenientValues.set(name, String(values[i])));
|
|
1610
|
-
}
|
|
1611
|
-
const parts = [];
|
|
1612
|
-
const indices = match.indices;
|
|
1613
|
-
const groupIndices = indices?.groups;
|
|
1614
|
-
let consumed = 0;
|
|
1615
|
-
for (const { name, token } of pattern.groups) {
|
|
1616
|
-
const raw = lenientValues.get(name) ?? match.groups[name];
|
|
1617
|
-
const fromIndices = !lenientValues.has(name) && groupIndices?.[name];
|
|
1618
|
-
const position = fromIndices ? fromIndices[0] : (match.index ?? 0) + consumed;
|
|
1619
|
-
parts.push({ token, raw, position });
|
|
1620
|
-
consumed += raw.length;
|
|
1621
|
-
}
|
|
1622
|
-
return parts;
|
|
1623
|
-
}
|
|
1624
|
-
function compileParser(formatStr, options = {}) {
|
|
1625
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
1626
|
-
throw new FormatSyntaxError({
|
|
1627
|
-
format: formatStr,
|
|
1628
|
-
message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
1632
|
-
const pattern = getPattern(formatStr, locale);
|
|
1633
|
-
return {
|
|
1634
|
-
formatStr,
|
|
1635
|
-
pattern,
|
|
1636
|
-
parse(input, opts = {}) {
|
|
1637
|
-
return parse(formatStr, input, { locale, ...opts });
|
|
1638
|
-
},
|
|
1639
|
-
safeParse(input, opts = {}) {
|
|
1640
|
-
return safeParse(formatStr, input, { locale, ...opts });
|
|
1641
|
-
},
|
|
1642
|
-
tryParse(input, opts = {}) {
|
|
1643
|
-
return tryParse(formatStr, input, { locale, ...opts });
|
|
1644
|
-
},
|
|
1645
|
-
parseToParts(input, opts = {}) {
|
|
1646
|
-
return parseToParts(formatStr, input, { locale, ...opts });
|
|
1647
|
-
}
|
|
1648
|
-
};
|
|
1649
|
-
}
|
|
1650
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1651
|
-
0 && (module.exports = {
|
|
1652
|
-
compileParser,
|
|
1653
|
-
parse,
|
|
1654
|
-
parseToParts,
|
|
1655
|
-
safeParse,
|
|
1656
|
-
tryParse
|
|
1657
|
-
});
|
|
23
|
+
parameter that could be invalid. *//* c8 ignore stop @preserve */var R=class extends E{constructor(t){let{message:n,...r}=t;super(n??`input "${t.input??""}" is ambiguous${t.reason?`: ${t.reason}`:""}.`,{code:"AMBIGUOUS_INPUT",...r}),this.name="AmbiguousInputError"}},N=class extends E{constructor(t){let{message:n,...r}=t;super(n??`locale "${t.actual??""}" is not a valid BCP-47 tag${t.reason?`: ${t.reason}`:""}.`,{code:"INVALID_LOCALE",...r}),this.name="InvalidLocaleError"}};/* c8 ignore start @preserve -- unreachable from the current test suite,
|
|
24
|
+
see rationale above */function Me(e,t){let n=e.message;if(/unknown token|isn't a recognized token/.test(n))return new I({input:t.input,format:t.format,reason:n});if(/ambiguous/i.test(n))return new R({input:t.input,format:t.format,reason:n});if(/offset/.test(n)&&/out of range|exceeds|doesn't match the shape/i.test(n))return new F({input:t.input,format:t.format,reason:n});if(/no valid pattern matches/i.test(n))return new T({input:t.input,format:t.format,reason:n});if(/doesn't describe a valid date\/time|incomplete date|weekday token|quarter token/.test(n))return new L({input:t.input,format:t.format,reason:n});let r=n.toLowerCase();return r.includes("locale")&&(r.includes("produced no")||r.includes("not a valid"))||r.includes("cutoffs must be")?new N({input:t.input,format:t.format,reason:n}):/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(n)?new S({input:t.input,format:t.format,reason:n}):new T({input:t.input,format:t.format,reason:n})}/* c8 ignore stop @preserve */var Le=new Map;var tt=[];function Oe(e){tt.push(e)}function Q(e){return e.replace(/_/g,"-")}var K=new Map,nt=500;function C(e){let t=K.get(e);if(t!==void 0)return t;let n;try{n=new Intl.Locale(e.replace(/_/g,"-")).toString().toLowerCase()}catch{n=e}if(K.size>=nt){let r=K.keys().next().value;r!==void 0&&K.delete(r)}return K.set(e,n),n}function he(e){try{new Intl.Locale(Q(e))}catch{throw new N({actual:e,reason:"not a valid BCP-47 locale tag"})}}var W=new Map,rt=500;function z(e,t,n){let r=e.formatToParts(t),o=r.findIndex(p=>p.type===n);/* c8 ignore start @preserve -- defensive guard against a real but
|
|
25
|
+
unreproducible failure mode: an Intl implementation that omits the
|
|
26
|
+
requested part type entirely for some locale. Checked every locale
|
|
27
|
+
with unusual dayPeriod/weekday/month rendering available in this
|
|
28
|
+
runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)
|
|
29
|
+
against the exact formatter options getLocaleVocab uses (notably
|
|
30
|
+
hour12: true for the dayPeriod formatter, which is what makes every
|
|
31
|
+
locale here actually emit a dayPeriod part — omitting it is what
|
|
32
|
+
produced a false "gap" during investigation). None omit their part
|
|
33
|
+
on this runtime. A different ICU version or a non-Node Intl
|
|
34
|
+
implementation could plausibly behave differently, so this stays a
|
|
35
|
+
real check rather than an assertion. *//* c8 ignore start @preserve */if(o===-1)throw new N({message:`temporal-fmt: locale produced no "${n}" part while building match vocabulary.`});/* c8 ignore stop @preserve */let a=r[o].value,s=r[o-1],m=r[o+1];return s?.type==="literal"&&!/\s/.test(s.value)&&(a=s.value+a),m?.type==="literal"&&!/\s/.test(m.value)&&(a=a+m.value),a}function ie(e,t,n){let r=new Map;for(let o=0;o<e.length;o++){let a=r.get(e[o]);if(a!==void 0)throw new Error(`temporal-fmt: locale "${n}" renders ${t} index ${a} and ${o} identically ("${e[o]}"). parse() can't reliably tell these apart for this locale/token, so this combination isn't supported.`);r.set(e[o],o)}}function M(e){let t=C(e);return Le.get(t)}function V(e){he(e);let t=C(e),n=Le.get(t);if(n)return n;let r=W.get(t);if(r)return r;let o=Q(e),a=new Intl.DateTimeFormat(o,{month:"long",timeZone:"UTC"}),s=new Intl.DateTimeFormat(o,{month:"short",timeZone:"UTC"}),m=[],p=[];for(let y=0;y<12;y++){let b=new Date(Date.UTC(2020,y,1));m.push(z(a,b,"month")),p.push(z(s,b,"month"))}ie(m,"MMMM month",e),ie(p,"MMM month",e);let l=new Intl.DateTimeFormat(o,{weekday:"long",timeZone:"UTC"}),c=new Intl.DateTimeFormat(o,{weekday:"short",timeZone:"UTC"}),i=[],d=[];for(let y=0;y<7;y++){let b=new Date(Date.UTC(2024,0,1+y));i.push(z(l,b,"weekday")),d.push(z(c,b,"weekday"))}ie(i,"EEEE weekday",e),ie(d,"EEE weekday",e);let u=new Intl.DateTimeFormat(o,{hour:"numeric",hour12:!0,timeZone:"UTC"}),h=z(u,new Date(Date.UTC(2020,0,1,1)),"dayPeriod"),k=z(u,new Date(Date.UTC(2020,0,1,13)),"dayPeriod"),$=[...new Set([h,k])],O={monthLong:m,monthShort:p,weekdayLong:i,weekdayShort:d,dayPeriod:$};if(W.size>=rt){let y=W.keys().next().value;y!==void 0&&W.delete(y)}return W.set(t,O),O}var ot=[0,31,59,90,120,151,181,212,243,273,304,334];function Pe(e){return e%4===0&&e%100!==0||e%400===0}function j(e){return Pe(e)?366:365}function q(e,t,n){let r=ot[t-1]+n;return t>2&&Pe(e)&&(r+=1),r}var ye=2e3;function at(e){let t=0;if(e>=ye)for(let r=ye;r<e;r++)t+=j(r);else for(let r=e;r<ye;r++)t-=j(r);return((5+t)%7+7)%7+1}function we(e,t,n,r){let a=q(e,t,n)+(4-r),s,m;a<1?(s=e-1,m=a+j(s)):a>j(e)?(s=e+1,m=a-j(e)):(s=e,m=a);let l=1+(4-at(s)+7)%7,c=1+Math.floor((m-l)/7);return{isoYear:s,week:c}}function g(e,t){let n=e<0,r=String(Math.abs(e)).padStart(t,"0");return n?"-"+r:r}g.fraction=function(t,n){let r=t.millisecond*1e6+(t.microsecond??0)*1e3+(t.nanosecond??0);return g(r,9).slice(0,n)};var me="en-US",B=new Map,st=500;function Ie(e,t){let n=JSON.stringify([C(e),t]),r=B.get(n);if(r)return r;if(B.size>=st){let o=B.keys().next().value;o!==void 0&&B.delete(o)}try{r=new Intl.DateTimeFormat(Q(e),t)}catch(o){throw new N({actual:e,reason:o.message})}return B.set(n,r),r}var J;De(()=>{J=void 0});function it(){if(J===void 0){J=!1;try{let e=se();new Intl.DateTimeFormat("en-US",{day:"numeric"}).formatToParts(e.PlainDate.from({year:1970,month:1,day:1})),J=!0}catch{}}return J}function ee(e,t,n,r){let o=e?.calendarId,a={...n,calendar:o&&o!=="iso8601"?o:"gregory"},s=e.toLocaleString;if(typeof s!="function"||s===Object.prototype.toLocaleString)throw new Error(`temporal-fmt: locale-aware part "${r}" needs a value that implements toLocaleString (a real Temporal object). A plain field bag cannot render locale-aware names \u2014 pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`);if(!it())try{return s.call(e,Q(t),a)}catch(y){throw y instanceof RangeError?new N({actual:t,reason:y.message}):y}let{toInstant:m,timeZoneId:p}=e,l=typeof m=="function"&&typeof p=="string",c=l?e.toInstant():e,i={...a,...l?{timeZone:p}:{}},u=Ie(t,i).formatToParts(c),h=u.findIndex(y=>y.type===r);if(h===-1)throw new Error(`temporal-fmt: locale "${t}" produced no "${r}" part for this token. This usually means the Temporal object is missing the field the token needs.`);let k=u[h].value,$=u[h-1],O=u[h+1];return $?.type==="literal"&&!/\s/.test($.value)&&(k=$.value+k),O?.type==="literal"&&!/\s/.test(O.value)&&(k=k+O.value),k}function mt(e,t){let n=M(t);if(n)return e<12?n.dayPeriod[0]:n.dayPeriod[1];let r=new Date(Date.UTC(1970,0,1,e)),a=Ie(t,{hour:"numeric",hour12:!0,timeZone:"UTC"}).formatToParts(r).find(s=>s.type==="dayPeriod");if(!a)throw new Error(`temporal-fmt: locale "${t}" produced no "dayPeriod" part for token "a".`);return a.value}function v(e,t,n,r,o,a){return o&&a!==void 0&&a>=0&&a<o.length?o[a]:ee(e,t,n,r)}function Z(e,t){if(e==="+00:00"&&(t==="X"||t==="XX"||t==="XXX"))return"Z";let n=e[0],r=e.slice(1,3),o=e.slice(4,6);switch(t){case"X":case"x":return o==="00"?`${n}${r}`:`${n}${r}${o}`;case"XX":case"xx":return`${n}${r}${o}`;case"XXX":case"xxx":return`${n}${r}:${o}`}}var Fe=[["yyyy",e=>g(e.year,4),"year"],["yy",e=>{if(e.year<0)throw new Error(`temporal-fmt: token "yy" doesn't support negative years (got ${e.year}), since truncating to 2 digits would make it indistinguishable from a positive year. Use "yyyy" instead.`);return g(e.year%100,2)},"year"],["MMMM",(e,t)=>{let n=M(t);return v(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["MMM",(e,t)=>{let n=M(t);return v(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["MM",e=>g(e.month,2),"month"],["M",e=>String(e.month),"month"],["dd",e=>g(e.day,2),"day"],["d",e=>String(e.day),"day"],["EEEE",(e,t)=>{let n=M(t);return v(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["EEE",(e,t)=>{let n=M(t);return v(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["HH",e=>g(e.hour,2),"hour"],["H",e=>String(e.hour),"hour"],["hh",e=>g(e.hour%12||12,2),"hour"],["h",e=>String(e.hour%12||12),"hour"],["mm",e=>g(e.minute,2),"minute"],["m",e=>String(e.minute),"minute"],["ss",e=>g(e.second,2),"second"],["s",e=>String(e.second),"second"],["SSSSSSSSS",e=>g.fraction(e,9),"millisecond"],["SSSSSSSS",e=>g.fraction(e,8),"millisecond"],["SSSSSSS",e=>g.fraction(e,7),"millisecond"],["SSSSSS",e=>g.fraction(e,6),"millisecond"],["SSSSS",e=>g.fraction(e,5),"millisecond"],["SSSS",e=>g.fraction(e,4),"millisecond"],["SSS",e=>g.fraction(e,3),"millisecond"],["SS",e=>g.fraction(e,2),"millisecond"],["S",e=>g.fraction(e,1),"millisecond"],["a",(e,t)=>mt(e.hour,t),"hour"],["zzz",e=>e.timeZoneId,"timeZoneId"],["xxx",e=>Z(e.offset,"xxx"),"offset"],["xx",e=>Z(e.offset,"xx"),"offset"],["X",e=>Z(e.offset,"X"),"offset"],["XX",e=>Z(e.offset,"XX"),"offset"],["XXX",e=>Z(e.offset,"XXX"),"offset"],["x",e=>Z(e.offset,"x"),"offset"],["do",e=>{let t=e.day,n=t%10,r=t%100;return r>=11&&r<=13?t+"th":n===1?t+"st":n===2?t+"nd":n===3?t+"rd":t+"th"},"day"],["Q",e=>String(Math.ceil(e.month/3)),"month"],["QQQ",e=>"Q"+Math.ceil(e.month/3),"month"],["ww",e=>{let{week:t}=we(e.year,e.month,e.day,e.dayOfWeek);return g(t,2)},"dayOfWeek"],["RRRR",e=>{let{isoYear:t}=we(e.year,e.month,e.day,e.dayOfWeek);return g(t,4)},"dayOfWeek"],["D",e=>String(q(e.year,e.month,e.day)),"day"],["DD",e=>g(q(e.year,e.month,e.day),2),"day"],["DDD",e=>g(q(e.year,e.month,e.day),3),"day"],["LLLL",(e,t)=>{let n=M(t);return v(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["LLL",(e,t)=>{let n=M(t);return v(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["cccc",(e,t)=>{let n=M(t);return v(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["ccc",(e,t)=>{let n=M(t);return v(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["GGGG",(e,t)=>ee(e,t,{era:"long"},"era"),"year"],["G",(e,t)=>ee(e,t,{era:"short"},"era"),"year"],["zzzz",(e,t)=>ee(e,t,{timeZoneName:"longGeneric"},"timeZoneName"),"timeZoneId"],["z",(e,t)=>ee(e,t,{timeZoneName:"short"},"timeZoneName"),"timeZoneId"]];var ct=Fe.map(([e])=>e).sort((e,t)=>t.length-e.length);function ve(e){let t=[],n=0;for(;n<e.length;){let r=e[n];if(r==="'"){if(e[n+1]==="'"){Se(t,"'"),n+=2;continue}let a=n+1,s="",m=!1;for(;a<e.length;){if(e[a]==="'"){if(e[a+1]==="'"){s+="'",a+=2;continue}m=!0,a+=1;break}s+=e[a],a+=1}if(!m)throw new S({format:e,message:`temporal-fmt: unterminated quote in format string "${e}"`});Se(t,s),n=a;continue}let o=ct.find(a=>e.startsWith(a,n));if(o){let a=o[o.length-1];if(e[n+o.length]===a){let s=n+o.length;for(;e[s]===a;)s+=1;throw new I({format:e,token:e.slice(n,s),message:`temporal-fmt: "${e.slice(n,s)}" in format string "${e}" isn't a recognized token \u2014 did you mean "${o}"?`})}t.push({kind:"token",value:o}),n+=o.length;continue}Se(t,r),n+=1}return t}function Se(e,t){let n=e[e.length-1];n&&n.kind==="literal"?n.value+=t:e.push({kind:"literal",value:t})}function ut(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function te(e,t=!1){let n=e.map(ut);return t?`(?:${n.map(dt).join("|")})`:`(?:${n.join("|")})`}function dt(e){return e.replace(/[a-zA-Z]/g,t=>`[${t.toLowerCase()}${t.toUpperCase()}]`)}var lt="(?:UTC|[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\d{1,2})?(?:\\/[A-Za-z0-9_+-]+)*)",gt={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 ft(){return lt}var ce;function pt(){return ce||(ce=new Set(Intl.supportedValuesOf("timeZone")),ce.add("UTC")),ce}var ht=/^[+-]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;function ke(e){return ht.test(e)||pt().has(e)}var yt={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]"},wt="Q[1-4]",St=new Set(["do","ww","RRRR","D","DD","DDD","LLLL","LLL","cccc","ccc","GGGG","G","zzzz","z"]),kt="-?\\d{4}",bt="-?\\d{4,}",be=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 ue(e,t,n){if(e==="yyyy")return n!==void 0&&be.has(n)?kt:bt;let r=yt[e];if(r)return r;if(e==="QQQ")return wt;if(St.has(e))throw new I({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 o=V(t);switch(e){case"MMMM":return te(o.monthLong);case"MMM":return te(o.monthShort);case"EEEE":return te(o.weekdayLong);case"EEE":return te(o.weekdayShort);case"a":return te(o.dayPeriod,!0);case"zzz":return ft();case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":return gt[e];default:throw new I({token:e,message:`temporal-fmt: unknown token "${e}"`})}}var Ae=new Set(["M","d","H","h","m","s"]),xt={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 xe(e,t){let n=new Map;function r(o,a){let s=`${o}:${a}`,m=n.get(s);if(m)return m;if(o===t.length){let i=a===e.length?[[]]:[];return n.set(s,i),i}let p=t[o],l=xt[p];/* c8 ignore start @preserve -- defensive guard, not reachable through
|
|
36
|
+
the public API. enumerateValidSplits's only caller (parse.ts, both
|
|
37
|
+
call sites) passes run.tokens straight from
|
|
38
|
+
pattern.ambiguousRuns, which parsePattern.ts only ever populates
|
|
39
|
+
with tokens already checked against UNPADDED_NUMERIC_TOKENS — the
|
|
40
|
+
exact same key set as UNPADDED_NUMERIC_RANGES. There's no path
|
|
41
|
+
where a token reaches here without having already passed that
|
|
42
|
+
check. */if(!l)throw new Error(`temporal-fmt: internal error \u2014 "${p}" is not an unpadded numeric token`);/* c8 ignore stop @preserve */let c=[];for(let{digits:i,min:d,max:u}of l){if(a+i>e.length)continue;let h=e.slice(a,a+i);if(i===2&&h[0]==="0")continue;let k=Number(h);if(!(k<d||k>u)){for(let $ of r(o+1,a+i))if(c.push([k,...$]),c.length===2)break;if(c.length===2)break}}return n.set(s,c),c}return r(0,0)}function Et(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var _e=12;function Tt(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):be.has(e.value)}function Ce(e,t){let n=[],r=[],o="",a=0,s=0,m={names:[],tokens:[]},p=l=>{if(m.tokens.length===1){let c=m.names[0],i=m.tokens[0];o+=`(?<${c}>${ue(i,t)})`,Ee(l)&&(s+=1)}else if(m.tokens.length>=2){let c=`r${a++}`,i=m.tokens.length;o+=`(?<${c}>\\d{${i},${i*2}})`,r.push({groupName:c,groupNames:m.names,tokens:m.tokens}),Ee(l)&&(s+=Tt(i+1))}m={names:[],tokens:[]}};for(let[l,c]of e.entries()){if(c.kind==="literal"){p(c),o+=Et(c.value);continue}if(Ae.has(c.value)){let h=`g${a++}`;n.push({name:h,token:c.value}),m.names.push(h),m.tokens.push(c.value);continue}p(c);let i=`g${a++}`;n.push({name:i,token:c.value});let d=e[l+1],u=d?.kind==="token"?d.value:void 0;c.value==="yyyy"&&u===void 0&&Ee(d)?o+=`(?<${i}>${ue(c.value,t,"M")})`:o+=`(?<${i}>${ue(c.value,t,u)})`}if(p(void 0),s>_e)throw new S({reason:`format string has too many variable-width numeric tokens glued to digit-consuming neighbors (ambiguity score ${s} > ${_e}). 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(`^(?:${o})$`,"ud"),groups:n,ambiguousRuns:r}}var $t=new Set(["latn","arab","deva","beng","guru","gujr","orya","tamldec","telu","knda","mlym","fullwide","hanidec"]),Xe=new Map;function Nt(e){let t=Xe.get(e);if(t)return t;/* c8 ignore start @preserve -- dead by construction, not just
|
|
43
|
+
untested: both callers (convertDigits, convertDigitsToAscii) already
|
|
44
|
+
return early on system === 'latn' before calling getDigitMap at all,
|
|
45
|
+
so this function is never invoked with 'latn'. Kept as a defensive
|
|
46
|
+
fallback rather than trusting that stays true for any future
|
|
47
|
+
caller. */if(e==="latn"){t={};for(let n=0;n<10;n++)t[String(n)]=String(n)}else{/* c8 ignore stop @preserve */let n=new Intl.NumberFormat("en-US-u-nu-"+e,{useGrouping:!1});t={};for(let r=0;r<10;r++)t[String(r)]=n.format(r)}return Xe.set(e,t),t}function Dt(e,t){if(t==="latn")return e;if(!$t.has(t))throw new N({actual:t,reason:`numbering system "${t}" is not supported.`});let n=Nt(t),r={};for(let a of Object.keys(n))r[n[a]]=a;let o="";for(let a of e)o+=r[a]??a;return o}function Te(e,t){let n=t.parseNumberingSystem??"latn";return n==="latn"?e:Dt(e,n)}var Y=new Map,Mt=500;Oe(()=>{Y.clear()});function Ne(e,t){let n=JSON.stringify([C(t),e]),r=Y.get(n);if(r)return r;if(Y.size>=Mt){let o=Y.keys().next().value;o!==void 0&&Y.delete(o)}return r=Ce(ve(e),t),Y.set(n,r),r}var U=new Map,Lt=500;function Ot(e){he(e);let t=C(e);if(U.has(t))return U.get(t);if(U.size>=Lt){let s=U.keys().next().value;s!==void 0&&U.delete(s)}let n,r=t.split("-"),o=r.indexOf("u"),a=o===-1?-1:r.indexOf("ca",o+1);if(a!==-1&&a+1<r.length){let s=new Intl.DateTimeFormat(t).resolvedOptions().calendar;n=s==="gregory"?void 0:s}return U.set(t,n),n}function Pt(e,t){if(e==="Z"){/* c8 ignore start @preserve -- unreachable: lowercase tokens' regex
|
|
48
|
+
(OFFSET_SHAPES in pattern.ts) has no "Z" alternative at all, so
|
|
49
|
+
raw === 'Z' can only ever be reached when token is one of the
|
|
50
|
+
uppercase variants (X/XX/XXX). A lowercase token can't even
|
|
51
|
+
capture "Z" as `raw` in the first place. */if(t==="x"||t==="xx"||t==="xxx")throw new Error(`temporal-fmt: offset token "${t}" 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 n=e[0];/* c8 ignore start @preserve -- unreachable: raw is a regex-captured
|
|
52
|
+
group from an offset token, and every OFFSET_SHAPES pattern
|
|
53
|
+
(pattern.ts) is anchored to either "Z" or a leading [+-]. raw's
|
|
54
|
+
first character can never be anything else by the time it reaches
|
|
55
|
+
this function. */if(n!=="+"&&n!=="-")throw new Error(`temporal-fmt: offset "${e}" for token "${t}" doesn't start with "+", "-", or "Z".`);/* c8 ignore stop @preserve */let r=e.slice(1),o,a;if(r.length===2){/* c8 ignore start @preserve -- unreachable: each offset token's own
|
|
56
|
+
regex shape in OFFSET_SHAPES (pattern.ts) already gates which
|
|
57
|
+
body shapes it can capture. Only X and x ever match a 2-digit
|
|
58
|
+
body — XX/xx/XXX/xxx's regexes can't produce one — so this
|
|
59
|
+
mismatch can never actually fire through parse(). */if(t!=="X"&&t!=="x")throw new Error(`temporal-fmt: offset token "${t}" can't match "${e}" \u2014 it requires minutes, but "${e}" has none.`);/* c8 ignore stop @preserve */o=r,a="00"}else if(r.length===4){/* c8 ignore start @preserve -- unreachable, same reason as the
|
|
60
|
+
2-digit case above: XXX/xxx's regex requires a colon, so it can
|
|
61
|
+
never capture a 4-digit no-colon body in the first place. */if(t==="XXX"||t==="xxx")throw new Error(`temporal-fmt: offset token "${t}" can't match "${e}" \u2014 it requires a colon between hours and minutes (e.g. "${n}${r.slice(0,2)}:${r.slice(2)}").`);/* c8 ignore stop @preserve */o=r.slice(0,2),a=r.slice(2,4)}else if(r.length===5&&r[2]===":"){/* c8 ignore start @preserve -- unreachable, same reason again: only
|
|
62
|
+
XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never
|
|
63
|
+
capture one. */if(t!=="XXX"&&t!=="xxx")throw new Error(`temporal-fmt: offset token "${t}" can't match "${e}" \u2014 it doesn't use a colon (use "${n}${r.slice(0,2)}${r.slice(3)}" instead).`);/* c8 ignore stop @preserve */o=r.slice(0,2),a=r.slice(3,5);/* c8 ignore start @preserve -- unreachable: every offset token's
|
|
64
|
+
regex only ever produces a body of length 2, length 4, or length 5
|
|
65
|
+
with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no
|
|
66
|
+
shape falls outside those three cases, so this else arm can't be
|
|
67
|
+
taken through parse(). Kept as an exhaustiveness fallback so
|
|
68
|
+
hoursStr/minutesStr are assigned on every path TypeScript can see. */}else throw new Error(`temporal-fmt: offset "${e}" doesn't match the shape token "${t}" accepts.`);/* c8 ignore stop @preserve */let s=Number(o),m=Number(a);if(s>14)throw new F({actual:e,message:`temporal-fmt: offset hours ${s} in "${e}" out of range (max 14 \u2014 Kiritimati, Line Islands is +14:00).`});if(m>59)throw new F({actual:e,message:`temporal-fmt: offset minutes ${m} in "${e}" out of range (max 59).`});if(n==="+"&&s===14&&m!==0)throw new F({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported UTC offset of +14:00.`});if(n==="-"&&s===12&&m!==0)throw new F({actual:e,message:`temporal-fmt: offset "${e}" exceeds the maximum supported negative UTC offset of -12:00.`});return`${n}${o}:${a}`}function f(e,t,n){e[t]=n}function It(e,t,n,r,o){let a=V(r);switch(t){case"yyyy":f(e,"year",Number(n));break;case"yy":f(e,"twoDigitYear",Number(n));break;case"MM":case"M":f(e,"month",Number(n));break;case"MMMM":f(e,"month",a.monthLong.indexOf(n)+1);break;case"MMM":f(e,"month",a.monthShort.indexOf(n)+1);break;case"dd":case"d":f(e,"day",Number(n));break;case"EEEE":f(e,"weekdayRaw",n),f(e,"weekdayExpected",a.weekdayLong.indexOf(n)+1);break;case"EEE":f(e,"weekdayRaw",n),f(e,"weekdayExpected",a.weekdayShort.indexOf(n)+1);break;case"HH":case"H":f(e,"hour",Number(n));break;case"hh":case"h":f(e,"hour12",Number(n));break;case"mm":case"m":f(e,"minute",Number(n));break;case"ss":case"s":f(e,"second",Number(n));break;case"S":case"SS":case"SSS":case"SSSS":case"SSSSS":case"SSSSSS":case"SSSSSSS":case"SSSSSSSS":case"SSSSSSSSS":{let s=Number(n.padEnd(9,"0"));f(e,"millisecond",Math.floor(s/1e6)),f(e,"microsecond",Math.floor(s/1e3)%1e3),f(e,"nanosecond",s%1e3);break}case"a":{let s=a.dayPeriod.findIndex(m=>m.toLowerCase()===n.toLowerCase());/* c8 ignore start @preserve -- unreachable: the 'a' token's regex
|
|
69
|
+
fragment (pattern.ts's alternation() over vocab.dayPeriod) can
|
|
70
|
+
only ever capture a case-insensitive match of one of
|
|
71
|
+
vocab.dayPeriod's own entries. Both the regex and this lookup
|
|
72
|
+
derive their vocab from the same `locale` via getLocaleVocab(),
|
|
73
|
+
so periodIndex can't come back negative through parse(). */if(s<0)throw new Error(`temporal-fmt: unknown day period "${n}" for locale "${r}".`);/* c8 ignore stop @preserve */f(e,"dayPeriodRaw",n),f(e,"isPM",s===1);break}case"zzz":f(e,"timeZoneId",n);break;case"X":case"XX":case"XXX":case"x":case"xx":case"xxx":f(e,"offsetString",Pt(n,t));break;case"Q":f(e,"quarter",Number(n));break;case"QQQ":f(e,"quarter",Number(n.slice(1)));break}}function Re(e,t){let n=t.indexOf("d");if(n!==-1){let r=e.filter(o=>o[n]<=12);if(r.length>0)return r[0]}return e[0]}function Ft(e){if(e.year!==void 0&&e.twoDigitYear!==void 0)throw new S({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 vt(e,t,n){if(e.hour!==void 0&&e.hour12!==void 0)throw new S({format:t,message:`temporal-fmt: format string "${t}" 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(n),o=e.hour<12?r.dayPeriod[0]:r.dayPeriod[1];if(e.dayPeriodRaw.toLowerCase()!==o?.toLowerCase())throw new S({format:t,message:`temporal-fmt: format string "${t}" 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 S({format:t,message:`temporal-fmt: format string "${t}" 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 de(e,t,n={}){if(e.length>1e3)throw new S({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(t.length>1e5)throw new S({input:t,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${t.length}).`});n.parseNumberingSystem&&(t=Te(t,n));let r=n.locale??me,o=Ot(r),a=Ne(e,r),s=a.regex.exec(t);if(!s)throw new T({input:t,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(a.groups.length===0)throw new T({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:w,token:x}of a.groups)if(x==="zzz"&&!ke(s.groups[w]))throw new G({input:t,format:e,actual:s.groups[w],reason:"not a recognized IANA time zone identifier"});let m=new Map;for(let w of a.ambiguousRuns){let x=s.groups[w.groupName],D=xe(x,w.tokens);if(D.length===0)throw new T({input:t,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(D.length>1){if(!n.lenient)throw new R({input:t,format:e,message:`temporal-fmt: "${x}" in format string "${e}" is ambiguous \u2014 ${D.length} different ways to read tokens "${w.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(D[0])} vs ${JSON.stringify(D[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 X=Re(D,w.tokens);w.groupNames.forEach((fe,He)=>m.set(fe,String(X[He])))}else w.groupNames.forEach((X,fe)=>m.set(X,String(D[0][fe])))}let p={};for(let{name:w,token:x}of a.groups){let D=m.get(w)??s.groups[w];It(p,x,D,r,e)}let l=Ft(p),c=vt(p,e,r),{month:i,day:d,minute:u,second:h,millisecond:k,microsecond:$,nanosecond:O,timeZoneId:y,offsetString:b,weekdayExpected:le,weekdayRaw:Ue,quarter:ge}=p,Ye=l!==void 0||i!==void 0||d!==void 0,A=l!==void 0&&i!==void 0&&d!==void 0;if(Ye&&!A)throw new L({format:e,message:`temporal-fmt: format string "${e}" has an incomplete date \u2014 year, month, and day tokens must all be present together.`});let re=c!==void 0||u!==void 0||h!==void 0||k!==void 0;if(y!==void 0&&!(A&&re))throw new S({format:e,message:`temporal-fmt: format string "${e}" has a "zzz" token but needs a full date and time to build a ZonedDateTime.`});if(b!==void 0&&!(A&&re))throw new S({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(le!==void 0&&!A)throw new S({format:e,message:`temporal-fmt: format string "${e}" has a weekday token ("EEEE"/"EEE") but needs a full date to validate it against.`});if(!A&&!re)throw new S({format:e,message:`temporal-fmt: format string "${e}" has no date or time tokens to parse.`});let H=se(),_={hour:c??0,minute:u??0,second:h??0,millisecond:k??0,microsecond:$??0,nanosecond:O??0},oe=o?{calendar:o}:{},ae={overflow:"reject"},P;try{if(y!==void 0){let w={overflow:"reject",offset:"prefer"};if(P=H.ZonedDateTime.from({year:l,month:i,day:d,..._,...oe,timeZone:y,...b!==void 0?{offset:b}:{}},w),b!==void 0){let x=P;if(x.hour!==_.hour||x.minute!==_.minute||x.second!==_.second)throw new L({input:t,format:e,message:`"${y}" has no such wall-clock time on this date \u2014 it falls in a DST gap, not an ambiguous or valid instant.`});let X=x.offset;if(X!==b)throw new T({input:t,format:e,message:`has both a "zzz" zone (${y}) and an offset token (${b}), but the zone's actual offset at this date/time is ${X}, not ${b}.`})}}else b!==void 0?P=H.ZonedDateTime.from({year:l,month:i,day:d,..._,...oe,timeZone:b},ae):A&&re?P=H.PlainDateTime.from({year:l,month:i,day:d,..._,...oe},ae):A?P=H.PlainDate.from({year:l,month:i,day:d,...oe},ae):P=H.PlainTime.from(_,ae)}catch(w){throw new L({input:t,format:e,message:`temporal-fmt: "${t}" doesn't describe a valid date/time for format "${e}": ${w.message}`})}if(le!==void 0){let w=P.dayOfWeek;if(w!==le){let x=V(r);throw new L({input:t,format:e,message:`temporal-fmt: "${Ue}" doesn't match the actual weekday (${x.weekdayLong[w-1]}) for the parsed date.`})}}if(ge!==void 0&&i!==void 0){let w=Math.ceil(i/3);if(ge!==w)throw new L({format:e,message:`temporal-fmt: format string "${e}" contains a quarter token (Q/QQQ) whose value (Q${ge}) disagrees with the parsed month's actual quarter \u2014 month ${i} is in Q${w}.`})}return P}function ze(e,t,n={}){try{return{ok:!0,value:de(e,t,n)}}catch(r){/* c8 ignore start @preserve */return r instanceof E?{ok:!1,error:r}:{ok:!1,error:Me(r,{input:t,format:e})}}/* c8 ignore stop @preserve */}function Ve(e,t,n={}){try{return de(e,t,n)}catch{return}}function Ze(e,t,n={}){if(e.length>1e3)throw new S({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});if(t.length>1e5)throw new S({input:t,message:`temporal-fmt: input exceeds maximum length of ${1e5} characters (got ${t.length}).`});n.parseNumberingSystem&&(t=Te(t,n));let r=n.locale??me,o=Ne(e,r),a=o.regex.exec(t);if(!a)throw new T({input:t,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 T({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:i,token:d}of o.groups)if(d==="zzz"&&!ke(a.groups[i]))throw new G({input:t,format:e,actual:a.groups[i],reason:"not a recognized IANA time zone identifier"});let s=new Map;for(let i of o.ambiguousRuns){let d=a.groups[i.groupName],u=xe(d,i.tokens);if(u.length===0)throw new T({input:t,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(u.length>1){if(!n.lenient)throw new R({input:t,format:e,message:`temporal-fmt: "${d}" in format string "${e}" is ambiguous \u2014 ${u.length} different ways to read tokens "${i.tokens.join("")}" (with no separator between them) are all individually valid (e.g. ${JSON.stringify(u[0])} vs ${JSON.stringify(u[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 h=Re(u,i.tokens);i.groupNames.forEach((k,$)=>s.set(k,String(h[$])))}else i.groupNames.forEach((h,k)=>s.set(h,String(u[0][k])))}let m=[],l=a.indices?.groups,c=0;for(let{name:i,token:d}of o.groups){let u=s.get(i)??a.groups[i],h=!s.has(i)&&l?.[i],k=h?h[0]:(a.index??0)+c;m.push({token:d,raw:u,position:k}),c+=u.length}return m}function At(e,t={}){if(e.length>1e3)throw new S({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});let n=t.locale??me,r=Ne(e,n);return{formatStr:e,pattern:r,parse(o,a={}){return de(e,o,{locale:n,...a})},safeParse(o,a={}){return ze(e,o,{locale:n,...a})},tryParse(o,a={}){return Ve(e,o,{locale:n,...a})},parseToParts(o,a={}){return Ze(e,o,{locale:n,...a})}}}0&&(module.exports={compileParser,parse,parseToParts,safeParse,tryParse});
|
|
1658
74
|
//# sourceMappingURL=parse.cjs.map
|