temporal-fmt 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -304
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,333 +1,2 @@
|
|
|
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/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
format: () => format,
|
|
24
|
-
matchesFormat: () => matchesFormat
|
|
25
|
-
});
|
|
26
|
-
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
|
|
28
|
-
// src/tokens.ts
|
|
29
|
-
function pad(n, len) {
|
|
30
|
-
return String(n).padStart(len, "0");
|
|
31
|
-
}
|
|
32
|
-
var DEFAULT_LOCALE = "en-US";
|
|
33
|
-
var formatterCache = /* @__PURE__ */ new Map();
|
|
34
|
-
var MAX_CACHE_SIZE = 500;
|
|
35
|
-
function getFormatter(locale, options) {
|
|
36
|
-
const key = locale + JSON.stringify(options);
|
|
37
|
-
let formatter = formatterCache.get(key);
|
|
38
|
-
if (formatter) {
|
|
39
|
-
return formatter;
|
|
40
|
-
}
|
|
41
|
-
if (formatterCache.size >= MAX_CACHE_SIZE) {
|
|
42
|
-
const oldestKey = formatterCache.keys().next().value;
|
|
43
|
-
if (oldestKey !== void 0) formatterCache.delete(oldestKey);
|
|
44
|
-
}
|
|
45
|
-
formatter = new Intl.DateTimeFormat(locale, options);
|
|
46
|
-
formatterCache.set(key, formatter);
|
|
47
|
-
return formatter;
|
|
48
|
-
}
|
|
49
|
-
function intlPart(temporal, locale, options, partType) {
|
|
50
|
-
const { toInstant, timeZoneId } = temporal;
|
|
51
|
-
const isZoned = typeof toInstant === "function" && typeof timeZoneId === "string";
|
|
52
|
-
const intlSafeTemporal = isZoned ? temporal.toInstant() : temporal;
|
|
53
|
-
const calendar = temporal?.calendarId;
|
|
54
|
-
const formatterOptions = {
|
|
55
|
-
...options,
|
|
56
|
-
...calendar && calendar !== "iso8601" ? { calendar } : {},
|
|
57
|
-
...isZoned ? { timeZone: timeZoneId } : {}
|
|
58
|
-
};
|
|
59
|
-
const formatter = getFormatter(locale, formatterOptions);
|
|
60
|
-
const parts = formatter.formatToParts(intlSafeTemporal);
|
|
61
|
-
const part = parts.find((p) => p.type === partType);
|
|
62
|
-
if (!part) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`temporal-fmt: locale "${locale}" produced no "${partType}" part for this token. This usually means the Temporal object is missing the field the token needs.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
return part.value;
|
|
68
|
-
}
|
|
69
|
-
var TOKENS = [
|
|
70
|
-
["yyyy", (t) => pad(t.year, 4), "year"],
|
|
71
|
-
["yy", (t) => {
|
|
72
|
-
if (t.year < 0) {
|
|
73
|
-
throw new Error(
|
|
74
|
-
`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.`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
return pad(t.year % 100, 2);
|
|
78
|
-
}, "year"],
|
|
79
|
-
["MMMM", (t, locale) => intlPart(t, locale, { month: "long" }, "month"), "month"],
|
|
80
|
-
["MMM", (t, locale) => intlPart(t, locale, { month: "short" }, "month"), "month"],
|
|
81
|
-
["MM", (t) => pad(t.month, 2), "month"],
|
|
82
|
-
["M", (t) => String(t.month), "month"],
|
|
83
|
-
["dd", (t) => pad(t.day, 2), "day"],
|
|
84
|
-
["d", (t) => String(t.day), "day"],
|
|
85
|
-
["EEEE", (t, locale) => intlPart(t, locale, { weekday: "long" }, "weekday"), "dayOfWeek"],
|
|
86
|
-
["EEE", (t, locale) => intlPart(t, locale, { weekday: "short" }, "weekday"), "dayOfWeek"],
|
|
87
|
-
["HH", (t) => pad(t.hour, 2), "hour"],
|
|
88
|
-
["H", (t) => String(t.hour), "hour"],
|
|
89
|
-
["hh", (t) => pad(t.hour % 12 || 12, 2), "hour"],
|
|
90
|
-
["h", (t) => String(t.hour % 12 || 12), "hour"],
|
|
91
|
-
["mm", (t) => pad(t.minute, 2), "minute"],
|
|
92
|
-
["m", (t) => String(t.minute), "minute"],
|
|
93
|
-
["ss", (t) => pad(t.second, 2), "second"],
|
|
94
|
-
["s", (t) => String(t.second), "second"],
|
|
95
|
-
["SSS", (t) => pad(t.millisecond, 3), "millisecond"],
|
|
96
|
-
// dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but
|
|
97
|
-
// still needs .hour on the input to compute which period it is
|
|
98
|
-
["a", (t, locale) => intlPart(t, locale, { hour: "numeric", hour12: true }, "dayPeriod"), "hour"],
|
|
99
|
-
["zzz", (t) => t.timeZoneId, "timeZoneId"]
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
// src/tokenize.ts
|
|
103
|
-
var SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);
|
|
104
|
-
function tokenize(format2) {
|
|
105
|
-
const pieces = [];
|
|
106
|
-
let i = 0;
|
|
107
|
-
while (i < format2.length) {
|
|
108
|
-
const ch = format2[i];
|
|
109
|
-
if (ch === "'") {
|
|
110
|
-
if (format2[i + 1] === "'") {
|
|
111
|
-
appendLiteral(pieces, "'");
|
|
112
|
-
i += 2;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
let j = i + 1;
|
|
116
|
-
let literal = "";
|
|
117
|
-
let closed = false;
|
|
118
|
-
while (j < format2.length) {
|
|
119
|
-
if (format2[j] === "'") {
|
|
120
|
-
if (format2[j + 1] === "'") {
|
|
121
|
-
literal += "'";
|
|
122
|
-
j += 2;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
closed = true;
|
|
126
|
-
j += 1;
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
literal += format2[j];
|
|
130
|
-
j += 1;
|
|
131
|
-
}
|
|
132
|
-
if (!closed) {
|
|
133
|
-
throw new Error(`temporal-fmt: unterminated quote in format string "${format2}"`);
|
|
134
|
-
}
|
|
135
|
-
appendLiteral(pieces, literal);
|
|
136
|
-
i = j;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const match = SORTED_TOKEN_STRINGS.find((tok) => format2.startsWith(tok, i));
|
|
140
|
-
if (match) {
|
|
141
|
-
pieces.push({ kind: "token", value: match });
|
|
142
|
-
i += match.length;
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
appendLiteral(pieces, ch);
|
|
146
|
-
i += 1;
|
|
147
|
-
}
|
|
148
|
-
return pieces;
|
|
149
|
-
}
|
|
150
|
-
function appendLiteral(pieces, value) {
|
|
151
|
-
const last = pieces[pieces.length - 1];
|
|
152
|
-
if (last && last.kind === "literal") {
|
|
153
|
-
last.value += value;
|
|
154
|
-
} else {
|
|
155
|
-
pieces.push({ kind: "literal", value });
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// src/constants.ts
|
|
160
|
-
var MAX_FORMAT_LENGTH = 1e3;
|
|
161
|
-
|
|
162
|
-
// src/format.ts
|
|
163
|
-
var HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));
|
|
164
|
-
function format(temporal, formatStr, options = {}) {
|
|
165
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
171
|
-
const pieces = tokenize(formatStr);
|
|
172
|
-
let result = "";
|
|
173
|
-
for (const piece of pieces) {
|
|
174
|
-
if (piece.kind === "literal") {
|
|
175
|
-
result += piece.value;
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
const handler = HANDLER_BY_TOKEN.get(piece.value);
|
|
179
|
-
if (!handler) {
|
|
180
|
-
throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
|
|
181
|
-
}
|
|
182
|
-
if (temporal[handler.field] === void 0) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
result += handler.fn(temporal, locale);
|
|
188
|
-
}
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// src/localeVocab.ts
|
|
193
|
-
var vocabCache = /* @__PURE__ */ new Map();
|
|
194
|
-
function partValue(formatter, date, type) {
|
|
195
|
-
const part = formatter.formatToParts(date).find((p) => p.type === type);
|
|
196
|
-
if (!part) {
|
|
197
|
-
throw new Error(`temporal-fmt: locale produced no "${type}" part while building match vocabulary.`);
|
|
198
|
-
}
|
|
199
|
-
return part.value;
|
|
200
|
-
}
|
|
201
|
-
function getLocaleVocab(locale) {
|
|
202
|
-
const cached = vocabCache.get(locale);
|
|
203
|
-
if (cached) {
|
|
204
|
-
return cached;
|
|
205
|
-
}
|
|
206
|
-
const monthLongFmt = new Intl.DateTimeFormat(locale, { month: "long", timeZone: "UTC" });
|
|
207
|
-
const monthShortFmt = new Intl.DateTimeFormat(locale, { month: "short", timeZone: "UTC" });
|
|
208
|
-
const monthLong = [];
|
|
209
|
-
const monthShort = [];
|
|
210
|
-
for (let m = 0; m < 12; m++) {
|
|
211
|
-
const date = new Date(Date.UTC(2020, m, 1));
|
|
212
|
-
monthLong.push(partValue(monthLongFmt, date, "month"));
|
|
213
|
-
monthShort.push(partValue(monthShortFmt, date, "month"));
|
|
214
|
-
}
|
|
215
|
-
const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: "long", timeZone: "UTC" });
|
|
216
|
-
const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
|
|
217
|
-
const weekdayLong = [];
|
|
218
|
-
const weekdayShort = [];
|
|
219
|
-
for (let d = 0; d < 7; d++) {
|
|
220
|
-
const date = new Date(Date.UTC(2024, 0, 1 + d));
|
|
221
|
-
weekdayLong.push(partValue(weekdayLongFmt, date, "weekday"));
|
|
222
|
-
weekdayShort.push(partValue(weekdayShortFmt, date, "weekday"));
|
|
223
|
-
}
|
|
224
|
-
const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: "numeric", hour12: true, timeZone: "UTC" });
|
|
225
|
-
const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), "dayPeriod");
|
|
226
|
-
const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), "dayPeriod");
|
|
227
|
-
const dayPeriod = [.../* @__PURE__ */ new Set([am, pm])];
|
|
228
|
-
const vocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };
|
|
229
|
-
vocabCache.set(locale, vocab);
|
|
230
|
-
return vocab;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// src/pattern.ts
|
|
234
|
-
function escapeRegExp(literal) {
|
|
235
|
-
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
236
|
-
}
|
|
237
|
-
function alternation(values) {
|
|
238
|
-
return `(?:${values.map(escapeRegExp).join("|")})`;
|
|
239
|
-
}
|
|
240
|
-
var timeZoneFragment;
|
|
241
|
-
function getTimeZoneFragment() {
|
|
242
|
-
if (timeZoneFragment) {
|
|
243
|
-
return timeZoneFragment;
|
|
244
|
-
}
|
|
245
|
-
const supportedValuesOf = Intl.supportedValuesOf;
|
|
246
|
-
if (typeof supportedValuesOf === "function") {
|
|
247
|
-
timeZoneFragment = alternation([...supportedValuesOf("timeZone"), "UTC"]);
|
|
248
|
-
} else {
|
|
249
|
-
timeZoneFragment = "[A-Za-z_]+(?:\\/[A-Za-z_+\\-0-9]+)+|UTC";
|
|
250
|
-
}
|
|
251
|
-
return timeZoneFragment;
|
|
252
|
-
}
|
|
253
|
-
var NUMERIC_FRAGMENTS = {
|
|
254
|
-
yyyy: "\\d{4}",
|
|
255
|
-
yy: "\\d{2}",
|
|
256
|
-
MM: "(?:0[1-9]|1[0-2])",
|
|
257
|
-
M: "(?:[1-9]|1[0-2])",
|
|
258
|
-
dd: "(?:0[1-9]|[12]\\d|3[01])",
|
|
259
|
-
d: "(?:[1-9]|[12]\\d|3[01])",
|
|
260
|
-
HH: "(?:[01]\\d|2[0-3])",
|
|
261
|
-
H: "(?:[0-9]|1\\d|2[0-3])",
|
|
262
|
-
hh: "(?:0[1-9]|1[0-2])",
|
|
263
|
-
h: "(?:[1-9]|1[0-2])",
|
|
264
|
-
mm: "(?:[0-5]\\d)",
|
|
265
|
-
m: "(?:[0-9]|[1-5]\\d)",
|
|
266
|
-
ss: "(?:[0-5]\\d)",
|
|
267
|
-
s: "(?:[0-9]|[1-5]\\d)",
|
|
268
|
-
SSS: "\\d{3}"
|
|
269
|
-
};
|
|
270
|
-
function tokenFragment(token, locale) {
|
|
271
|
-
const numeric = NUMERIC_FRAGMENTS[token];
|
|
272
|
-
if (numeric) {
|
|
273
|
-
return numeric;
|
|
274
|
-
}
|
|
275
|
-
const vocab = getLocaleVocab(locale);
|
|
276
|
-
switch (token) {
|
|
277
|
-
case "MMMM":
|
|
278
|
-
return alternation(vocab.monthLong);
|
|
279
|
-
case "MMM":
|
|
280
|
-
return alternation(vocab.monthShort);
|
|
281
|
-
case "EEEE":
|
|
282
|
-
return alternation(vocab.weekdayLong);
|
|
283
|
-
case "EEE":
|
|
284
|
-
return alternation(vocab.weekdayShort);
|
|
285
|
-
case "a":
|
|
286
|
-
return alternation(vocab.dayPeriod);
|
|
287
|
-
case "zzz":
|
|
288
|
-
return getTimeZoneFragment();
|
|
289
|
-
default:
|
|
290
|
-
throw new Error(`temporal-fmt: unknown token "${token}"`);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function buildPatternSource(pieces, locale) {
|
|
294
|
-
let source = "";
|
|
295
|
-
for (const piece of pieces) {
|
|
296
|
-
source += piece.kind === "literal" ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);
|
|
297
|
-
}
|
|
298
|
-
return `^(?:${source})$`;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// src/matchesFormat.ts
|
|
302
|
-
var patternCache = /* @__PURE__ */ new Map();
|
|
303
|
-
var MAX_CACHE_SIZE2 = 500;
|
|
304
|
-
function getPattern(formatStr, locale) {
|
|
305
|
-
const key = locale + "\0" + formatStr;
|
|
306
|
-
let pattern = patternCache.get(key);
|
|
307
|
-
if (pattern) {
|
|
308
|
-
return pattern;
|
|
309
|
-
}
|
|
310
|
-
if (patternCache.size >= MAX_CACHE_SIZE2) {
|
|
311
|
-
const oldestKey = patternCache.keys().next().value;
|
|
312
|
-
if (oldestKey !== void 0) patternCache.delete(oldestKey);
|
|
313
|
-
}
|
|
314
|
-
const source = buildPatternSource(tokenize(formatStr), locale);
|
|
315
|
-
pattern = new RegExp(source, "u");
|
|
316
|
-
patternCache.set(key, pattern);
|
|
317
|
-
return pattern;
|
|
318
|
-
}
|
|
319
|
-
function matchesFormat(formatStr, input, options = {}) {
|
|
320
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
321
|
-
throw new Error(
|
|
322
|
-
`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
326
|
-
return getPattern(formatStr, locale).test(input);
|
|
327
|
-
}
|
|
328
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
329
|
-
0 && (module.exports = {
|
|
330
|
-
format,
|
|
331
|
-
matchesFormat
|
|
332
|
-
});
|
|
1
|
+
"use strict";var S=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var z=(t,e)=>{for(var n in e)S(t,n,{get:e[n],enumerable:!0})},H=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of $(e))!U.call(t,r)&&r!==n&&S(t,r,{get:()=>e[r],enumerable:!(o=_(e,r))||o.enumerable});return t};var N=t=>H(S({},"__esModule",{value:!0}),t);var J={};z(J,{format:()=>x,matchesFormat:()=>Z});module.exports=N(J);function m(t,e){return String(t).padStart(e,"0")}var T="en-US",h=new Map,R=500;function V(t,e){let n=t+JSON.stringify(e),o=h.get(n);if(o)return o;if(h.size>=R){let r=h.keys().next().value;r!==void 0&&h.delete(r)}return o=new Intl.DateTimeFormat(t,e),h.set(n,o),o}function p(t,e,n,o){let{toInstant:r,timeZoneId:i}=t,a=typeof r=="function"&&typeof i=="string",s=a?t.toInstant():t,u=t?.calendarId,w={...n,...u&&u!=="iso8601"?{calendar:u}:{},...a?{timeZone:i}:{}},k=V(e,w).formatToParts(s).find(M=>M.type===o);if(!k)throw new Error(`temporal-fmt: locale "${e}" produced no "${o}" part for this token. This usually means the Temporal object is missing the field the token needs.`);return k.value}var E=[["yyyy",t=>m(t.year,4),"year"],["yy",t=>{if(t.year<0)throw new Error(`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.`);return m(t.year%100,2)},"year"],["MMMM",(t,e)=>p(t,e,{month:"long"},"month"),"month"],["MMM",(t,e)=>p(t,e,{month:"short"},"month"),"month"],["MM",t=>m(t.month,2),"month"],["M",t=>String(t.month),"month"],["dd",t=>m(t.day,2),"day"],["d",t=>String(t.day),"day"],["EEEE",(t,e)=>p(t,e,{weekday:"long"},"weekday"),"dayOfWeek"],["EEE",(t,e)=>p(t,e,{weekday:"short"},"weekday"),"dayOfWeek"],["HH",t=>m(t.hour,2),"hour"],["H",t=>String(t.hour),"hour"],["hh",t=>m(t.hour%12||12,2),"hour"],["h",t=>String(t.hour%12||12),"hour"],["mm",t=>m(t.minute,2),"minute"],["m",t=>String(t.minute),"minute"],["ss",t=>m(t.second,2),"second"],["s",t=>String(t.second),"second"],["SSS",t=>m(t.millisecond,3),"millisecond"],["a",(t,e)=>p(t,e,{hour:"numeric",hour12:!0},"dayPeriod"),"hour"],["zzz",t=>t.timeZoneId,"timeZoneId"]];var K=E.map(([t])=>t).sort((t,e)=>e.length-t.length);function F(t){let e=[],n=0;for(;n<t.length;){let o=t[n];if(o==="'"){if(t[n+1]==="'"){D(e,"'"),n+=2;continue}let i=n+1,a="",s=!1;for(;i<t.length;){if(t[i]==="'"){if(t[i+1]==="'"){a+="'",i+=2;continue}s=!0,i+=1;break}a+=t[i],i+=1}if(!s)throw new Error(`temporal-fmt: unterminated quote in format string "${t}"`);D(e,a),n=i;continue}let r=K.find(i=>t.startsWith(i,n));if(r){e.push({kind:"token",value:r}),n+=r.length;continue}D(e,o),n+=1}return e}function D(t,e){let n=t[t.length-1];n&&n.kind==="literal"?n.value+=e:t.push({kind:"literal",value:e})}var j=new Map(E.map(([t,e,n])=>[t,{fn:e,field:n}]));function x(t,e,n={}){if(e.length>1e3)throw new Error(`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`);let o=n.locale??T,r=F(e),i="";for(let a of r){if(a.kind==="literal"){i+=a.value;continue}let s=j.get(a.value);if(!s)throw new Error(`temporal-fmt: unknown token "${a.value}"`);if(t[s.field]===void 0)throw new Error(`temporal-fmt: token "${a.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`);i+=s.fn(t,o)}return i}var P=new Map;function l(t,e,n){let o=t.formatToParts(e).find(r=>r.type===n);if(!o)throw new Error(`temporal-fmt: locale produced no "${n}" part while building match vocabulary.`);return o.value}function C(t){let e=P.get(t);if(e)return e;let n=new Intl.DateTimeFormat(t,{month:"long",timeZone:"UTC"}),o=new Intl.DateTimeFormat(t,{month:"short",timeZone:"UTC"}),r=[],i=[];for(let c=0;c<12;c++){let g=new Date(Date.UTC(2020,c,1));r.push(l(n,g,"month")),i.push(l(o,g,"month"))}let a=new Intl.DateTimeFormat(t,{weekday:"long",timeZone:"UTC"}),s=new Intl.DateTimeFormat(t,{weekday:"short",timeZone:"UTC"}),u=[],w=[];for(let c=0;c<7;c++){let g=new Date(Date.UTC(2024,0,1+c));u.push(l(a,g,"weekday")),w.push(l(s,g,"weekday"))}let L=new Intl.DateTimeFormat(t,{hour:"numeric",hour12:!0,timeZone:"UTC"}),I=l(L,new Date(Date.UTC(2020,0,1,1)),"dayPeriod"),k=l(L,new Date(Date.UTC(2020,0,1,13)),"dayPeriod"),M=[...new Set([I,k])],O={monthLong:r,monthShort:i,weekdayLong:u,weekdayShort:w,dayPeriod:M};return P.set(t,O),O}function v(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function d(t){return`(?:${t.map(v).join("|")})`}var f;function G(){if(f)return f;let t=Intl.supportedValuesOf;return typeof t=="function"?f=d([...t("timeZone"),"UTC"]):f="[A-Za-z_]+(?:\\/[A-Za-z_+\\-0-9]+)+|UTC",f}var W={yyyy:"\\d{4}",yy:"\\d{2}",MM:"(?:0[1-9]|1[0-2])",M:"(?:[1-9]|1[0-2])",dd:"(?:0[1-9]|[12]\\d|3[01])",d:"(?:[1-9]|[12]\\d|3[01])",HH:"(?:[01]\\d|2[0-3])",H:"(?:[0-9]|1\\d|2[0-3])",hh:"(?:0[1-9]|1[0-2])",h:"(?:[1-9]|1[0-2])",mm:"(?:[0-5]\\d)",m:"(?:[0-9]|[1-5]\\d)",ss:"(?:[0-5]\\d)",s:"(?:[0-9]|[1-5]\\d)",SSS:"\\d{3}"};function X(t,e){let n=W[t];if(n)return n;let o=C(e);switch(t){case"MMMM":return d(o.monthLong);case"MMM":return d(o.monthShort);case"EEEE":return d(o.weekdayLong);case"EEE":return d(o.weekdayShort);case"a":return d(o.dayPeriod);case"zzz":return G();default:throw new Error(`temporal-fmt: unknown token "${t}"`)}}function A(t,e){let n="";for(let o of t)n+=o.kind==="literal"?v(o.value):X(o.value,e);return`^(?:${n})$`}var y=new Map,q=500;function B(t,e){let n=e+"\0"+t,o=y.get(n);if(o)return o;if(y.size>=q){let i=y.keys().next().value;i!==void 0&&y.delete(i)}let r=A(F(t),e);return o=new RegExp(r,"u"),y.set(n,o),o}function Z(t,e,n={}){if(t.length>1e3)throw new Error(`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${t.length}).`);let o=n.locale??T;return B(t,o).test(e)}0&&(module.exports={format,matchesFormat});
|
|
333
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/tokens.ts","../src/tokenize.ts","../src/constants.ts","../src/format.ts","../src/localeVocab.ts","../src/pattern.ts","../src/matchesFormat.ts"],"sourcesContent":["export { format } from './format.js';\nexport { matchesFormat } from './matchesFormat.js';\nexport type { TemporalLike, FormatOptions } from './tokens.js';","export function pad(n: number, len: number): string {\n return String(n).padStart(len, '0');\n}\n\n// Not every field exists on every Temporal type (PlainDate has no .hour,\n// etc). Callers check for undefined before formatting a token.\nexport interface TemporalLike {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n timeZoneId?: string;\n dayOfWeek?: number; // 1 (Mon) - 7 (Sun), per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n}\n\nexport interface FormatOptions {\n /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */\n locale?: string;\n}\n\nexport const DEFAULT_LOCALE = 'en-US';\n\n// Intl.DateTimeFormat is expensive to construct and format() can run in a\n// loop (rendering a table of dates), so cache by (locale, options).\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + JSON.stringify(options);\n let formatter = formatterCache.get(key);\n if (formatter) {\n return formatter;\n }\n if (formatterCache.size >= MAX_CACHE_SIZE) {\n // not real LRU, just evicts oldest insertion — fine for this key space\n const oldestKey = formatterCache.keys().next().value;\n if (oldestKey !== undefined) formatterCache.delete(oldestKey);\n }\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Pulls a single field out of formatToParts() rather than building a full\n// string and slicing it — slicing breaks under RTL and locales with\n// different field ordering.\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\n // formatToParts() throws on ZonedDateTime directly (per spec), so convert\n // to Instant and pass the zone via `timeZone` instead. Don't convert to\n // PlainDateTime — that drops the zone, which breaks 'MMMM' + 'zzz' combos.\n const { toInstant, timeZoneId } = temporal;\n const isZoned = typeof toInstant === 'function' && typeof timeZoneId === 'string';\n // has to be called as temporal.toInstant() — destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n\n // Intl throws \"Mismatching Calendars\" if the formatter's calendar doesn't\n // match the object's own (e.g. en-US formatter defaults to gregory, but\n // a hebrew/islamic PlainDate needs its own calendar passed through).\n //\n // skip this for iso8601 specifically — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes formatToParts()\n // come back empty. no idea why, cost me an hour.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n ...(calendar && calendar !== 'iso8601' ? { calendar } : {}),\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, formatterOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const part = parts.find((p) => p.type === partType);\n if (!part) {\n throw new Error(\n `temporal-fmt: locale \"${locale}\" produced no \"${partType}\" part for this token. ` +\n `This usually means the Temporal object is missing the field the token needs.`\n );\n }\n return part.value;\n}\n\ntype TokenHandler = (t: TemporalLike, locale: string) => string;\n\n// Longest-first — tokenizer is greedy, \"yyyy\" has to be tried before \"yy\".\n//\n// Numeric tokens always render in ASCII digits, never locale-native\n// (Arabic-Indic, Devanagari, etc). Padding non-ASCII digit strings to a\n// fixed width isn't the same operation as padding \"3\", and most consumers\n// parsing these back out want plain digits anyway.\nexport const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]> = [\n ['yyyy', (t) => pad(t.year!, 4), 'year'],\n ['yy', (t) => {\n // -45 % 100 === -45, so truncating negative years to 2 digits doesn't\n // work and Math.abs() would make 45 CE and 45 BCE render the same.\n if (t.year! < 0) {\n throw new Error(\n `temporal-fmt: token \"yy\" doesn't support negative years (got ${t.year}), ` +\n `since truncating to 2 digits would make it indistinguishable from a ` +\n `positive year. Use \"yyyy\" instead.`\n );\n }\n return pad(t.year! % 100, 2);\n }, 'year'],\n ['MMMM', (t, locale) => intlPart(t, locale, { month: 'long' }, 'month'), 'month'],\n ['MMM', (t, locale) => intlPart(t, locale, { month: 'short' }, 'month'), 'month'],\n ['MM', (t) => pad(t.month!, 2), 'month'],\n ['M', (t) => String(t.month!), 'month'],\n ['dd', (t) => pad(t.day!, 2), 'day'],\n ['d', (t) => String(t.day!), 'day'],\n ['EEEE', (t, locale) => intlPart(t, locale, { weekday: 'long' }, 'weekday'), 'dayOfWeek'],\n ['EEE', (t, locale) => intlPart(t, locale, { weekday: 'short' }, 'weekday'), 'dayOfWeek'],\n ['HH', (t) => pad(t.hour!, 2), 'hour'],\n ['H', (t) => String(t.hour!), 'hour'],\n ['hh', (t) => pad(t.hour! % 12 || 12, 2), 'hour'],\n ['h', (t) => String(t.hour! % 12 || 12), 'hour'],\n ['mm', (t) => pad(t.minute!, 2), 'minute'],\n ['m', (t) => String(t.minute!), 'minute'],\n ['ss', (t) => pad(t.second!, 2), 'second'],\n ['s', (t) => String(t.second!), 'second'],\n ['SSS', (t) => pad(t.millisecond!, 3), 'millisecond'],\n // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but\n // still needs .hour on the input to compute which period it is\n ['a', (t, locale) => intlPart(t, locale, { hour: 'numeric', hour12: true }, 'dayPeriod'), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n];\n","import { TOKENS } from './tokens.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in format string \"${format}\"`);\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is \n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}\n","// format strings are short hand-written literals (\"yyyy-MM-dd\")\n// cap the length so a bug or bad input can't make tokenize() do unbounded work\nexport const MAX_FORMAT_LENGTH = 1000;","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n/**\n * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime\n * using a date-fns-style token string.\n *\n * @example\n * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // \"2026-08-04\"\n * format(zdt, \"MMM d, yyyy 'at' h:mm a\") // \"Aug 4, 2026 at 3:45 PM\"\n * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // \"août 4, 2026\"\n * format(zdt, 'EEEE d MMMM', { locale: 'ar-EG' }) // Arabic weekday/month names\n *\n * Numeric fields always render in ASCII digits regardless of locale.\n * Named fields (MMMM, EEEE, a) are fully localized via Intl, including\n * non-Gregorian calendars if the Temporal object carries one.\n *\n * Throws if the format string uses a token the input type doesn't support\n * (e.g. 'HH' on a PlainDate) rather than silently printing \"undefined\".\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: FormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = tokenize(formatStr);\n let result = '';\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n if (!handler) {\n // shouldn't happen — tokenize() only emits tokens from TOKENS\n throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n }\n\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`\n );\n }\n\n result += handler.fn(temporal, locale);\n }\n\n return result;\n}\n","// Closed-vocabulary lookups for locale-aware tokens (MMMM, MMM, EEEE, EEE, a).\n// Each set is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nexport interface LocaleVocab {\n monthLong: string[]; // index 0 = January\n monthShort: string[];\n weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n weekdayShort: string[];\n dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\n\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const part = formatter.formatToParts(date).find((p) => p.type === type);\n if (!part) {\n throw new Error(`temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`);\n }\n return part.value;\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n const cached = vocabCache.get(locale);\n if (cached) {\n return cached;\n }\n\n const monthLongFmt = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const monthLong: string[] = [];\n const monthShort: string[] = [];\n for (let m = 0; m < 12; m++) {\n const date = new Date(Date.UTC(2020, m, 1));\n monthLong.push(partValue(monthLongFmt, date, 'month'));\n monthShort.push(partValue(monthShortFmt, date, 'month'));\n }\n\n const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayLong: string[] = [];\n const weekdayShort: string[] = [];\n // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n for (let d = 0; d < 7; d++) {\n const date = new Date(Date.UTC(2024, 0, 1 + d));\n weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n }\n\n const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n const dayPeriod = [...new Set([am, pm])];\n\n const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n vocabCache.set(locale, vocab);\n return vocab;\n}\n","import type { Piece } from './tokenize.js';\nimport { getLocaleVocab } from './localeVocab.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[]): string {\n return `(?:${values.map(escapeRegExp).join('|')})`;\n}\n\nlet timeZoneFragment: string | undefined;\n\nfunction getTimeZoneFragment(): string {\n if (timeZoneFragment) {\n return timeZoneFragment;\n }\n const supportedValuesOf = (Intl as unknown as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf;\n if (typeof supportedValuesOf === 'function') {\n // supportedValuesOf('timeZone') leaves out 'UTC', but format() can\n // produce it from a real ZonedDateTime — without this, matchesFormat\n // rejected our own library's own output.\n timeZoneFragment = alternation([...supportedValuesOf('timeZone'), 'UTC']);\n } else {\n // no Intl.supportedValuesOf — match on shape only\n timeZoneFragment = '[A-Za-z_]+(?:\\\\/[A-Za-z_+\\\\-0-9]+)+|UTC';\n }\n return timeZoneFragment;\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yyyy: '\\\\d{4}',\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:[1-9]|1[0-2])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[1-9]|[12]\\\\d|3[01])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:[0-9]|1\\\\d|2[0-3])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:[1-9]|1[0-2])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[0-9]|[1-5]\\\\d)',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[0-9]|[1-5]\\\\d)',\n SSS: '\\\\d{3}',\n};\n\nfunction tokenFragment(token: string, locale: string): string {\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'MMMM': return alternation(vocab.monthLong);\n case 'MMM': return alternation(vocab.monthShort);\n case 'EEEE': return alternation(vocab.weekdayLong);\n case 'EEE': return alternation(vocab.weekdayShort);\n case 'a': return alternation(vocab.dayPeriod);\n case 'zzz': return getTimeZoneFragment();\n default:\n throw new Error(`temporal-fmt: unknown token \"${token}\"`);\n }\n}\n\n// Builds one anchored regex from tokenize() output — matches exactly what\n// format() could have produced for this format string, in this locale.\nexport function buildPatternSource(pieces: Piece[], locale: string): string {\n let source = '';\n for (const piece of pieces) {\n source += piece.kind === 'literal' ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);\n }\n return `^(?:${source})$`;\n}\n","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildPatternSource } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst patternCache = new Map<string, RegExp>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getPattern(formatStr: string, locale: string): RegExp {\n // \\0 can't appear in a locale tag or format string, so it's a safe join char\n const key = locale + '\\0' + formatStr;\n let pattern = patternCache.get(key);\n if (pattern) {\n return pattern;\n }\n if (patternCache.size >= MAX_CACHE_SIZE) {\n const oldestKey = patternCache.keys().next().value;\n if (oldestKey !== undefined) patternCache.delete(oldestKey);\n }\n const source = buildPatternSource(tokenize(formatStr), locale);\n pattern = new RegExp(source, 'u');\n patternCache.set(key, pattern);\n return pattern;\n}\n\n/**\n * Checks if `input` could plausibly be format()'s output for this format\n * string. Shape and vocabulary only — no parsing, and Feb 30 still passes.\n *\n * @example\n * matchesFormat('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // true\n * matchesFormat('yyyy-MM', '2026-08-04T15:45:30') // false\n */\nexport function matchesFormat(formatStr: string, input: string, options: FormatOptions = {}): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n return getPattern(formatStr, locale).test(input);\n}\n"],"mappings":"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","names":["format","MAX_CACHE_SIZE"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tokens.ts","../src/tokenize.ts","../src/format.ts","../src/localeVocab.ts","../src/pattern.ts","../src/matchesFormat.ts"],"sourcesContent":["export { format } from './format.js';\nexport { matchesFormat } from './matchesFormat.js';\nexport type { TemporalLike, FormatOptions } from './tokens.js';","export function pad(n: number, len: number): string {\n return String(n).padStart(len, '0');\n}\n\n// Not every field exists on every Temporal type (PlainDate has no .hour,\n// etc). Callers check for undefined before formatting a token.\nexport interface TemporalLike {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n timeZoneId?: string;\n dayOfWeek?: number; // 1 (Mon) - 7 (Sun), per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n}\n\nexport interface FormatOptions {\n /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */\n locale?: string;\n}\n\nexport const DEFAULT_LOCALE = 'en-US';\n\n// Intl.DateTimeFormat is expensive to construct and format() can run in a\n// loop (rendering a table of dates), so cache by (locale, options).\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + JSON.stringify(options);\n let formatter = formatterCache.get(key);\n if (formatter) {\n return formatter;\n }\n if (formatterCache.size >= MAX_CACHE_SIZE) {\n // not real LRU, just evicts oldest insertion — fine for this key space\n const oldestKey = formatterCache.keys().next().value;\n if (oldestKey !== undefined) formatterCache.delete(oldestKey);\n }\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Pulls a single field out of formatToParts() rather than building a full\n// string and slicing it — slicing breaks under RTL and locales with\n// different field ordering.\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\n // formatToParts() throws on ZonedDateTime directly (per spec), so convert\n // to Instant and pass the zone via `timeZone` instead. Don't convert to\n // PlainDateTime — that drops the zone, which breaks 'MMMM' + 'zzz' combos.\n const { toInstant, timeZoneId } = temporal;\n const isZoned = typeof toInstant === 'function' && typeof timeZoneId === 'string';\n // has to be called as temporal.toInstant() — destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n\n // Intl throws \"Mismatching Calendars\" if the formatter's calendar doesn't\n // match the object's own (e.g. en-US formatter defaults to gregory, but\n // a hebrew/islamic PlainDate needs its own calendar passed through).\n //\n // skip this for iso8601 specifically — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes formatToParts()\n // come back empty. no idea why, cost me an hour.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n ...(calendar && calendar !== 'iso8601' ? { calendar } : {}),\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, formatterOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const part = parts.find((p) => p.type === partType);\n if (!part) {\n throw new Error(\n `temporal-fmt: locale \"${locale}\" produced no \"${partType}\" part for this token. ` +\n `This usually means the Temporal object is missing the field the token needs.`\n );\n }\n return part.value;\n}\n\ntype TokenHandler = (t: TemporalLike, locale: string) => string;\n\n// Longest-first — tokenizer is greedy, \"yyyy\" has to be tried before \"yy\".\n//\n// Numeric tokens always render in ASCII digits, never locale-native\n// (Arabic-Indic, Devanagari, etc). Padding non-ASCII digit strings to a\n// fixed width isn't the same operation as padding \"3\", and most consumers\n// parsing these back out want plain digits anyway.\nexport const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]> = [\n ['yyyy', (t) => pad(t.year!, 4), 'year'],\n ['yy', (t) => {\n // -45 % 100 === -45, so truncating negative years to 2 digits doesn't\n // work and Math.abs() would make 45 CE and 45 BCE render the same.\n if (t.year! < 0) {\n throw new Error(\n `temporal-fmt: token \"yy\" doesn't support negative years (got ${t.year}), ` +\n `since truncating to 2 digits would make it indistinguishable from a ` +\n `positive year. Use \"yyyy\" instead.`\n );\n }\n return pad(t.year! % 100, 2);\n }, 'year'],\n ['MMMM', (t, locale) => intlPart(t, locale, { month: 'long' }, 'month'), 'month'],\n ['MMM', (t, locale) => intlPart(t, locale, { month: 'short' }, 'month'), 'month'],\n ['MM', (t) => pad(t.month!, 2), 'month'],\n ['M', (t) => String(t.month!), 'month'],\n ['dd', (t) => pad(t.day!, 2), 'day'],\n ['d', (t) => String(t.day!), 'day'],\n ['EEEE', (t, locale) => intlPart(t, locale, { weekday: 'long' }, 'weekday'), 'dayOfWeek'],\n ['EEE', (t, locale) => intlPart(t, locale, { weekday: 'short' }, 'weekday'), 'dayOfWeek'],\n ['HH', (t) => pad(t.hour!, 2), 'hour'],\n ['H', (t) => String(t.hour!), 'hour'],\n ['hh', (t) => pad(t.hour! % 12 || 12, 2), 'hour'],\n ['h', (t) => String(t.hour! % 12 || 12), 'hour'],\n ['mm', (t) => pad(t.minute!, 2), 'minute'],\n ['m', (t) => String(t.minute!), 'minute'],\n ['ss', (t) => pad(t.second!, 2), 'second'],\n ['s', (t) => String(t.second!), 'second'],\n ['SSS', (t) => pad(t.millisecond!, 3), 'millisecond'],\n // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but\n // still needs .hour on the input to compute which period it is\n ['a', (t, locale) => intlPart(t, locale, { hour: 'numeric', hour12: true }, 'dayPeriod'), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n];\n","import { TOKENS } from './tokens.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in format string \"${format}\"`);\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is \n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}\n","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n/**\n * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime\n * using a date-fns-style token string.\n *\n * @example\n * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // \"2026-08-04\"\n * format(zdt, \"MMM d, yyyy 'at' h:mm a\") // \"Aug 4, 2026 at 3:45 PM\"\n * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // \"août 4, 2026\"\n * format(zdt, 'EEEE d MMMM', { locale: 'ar-EG' }) // Arabic weekday/month names\n *\n * Numeric fields always render in ASCII digits regardless of locale.\n * Named fields (MMMM, EEEE, a) are fully localized via Intl, including\n * non-Gregorian calendars if the Temporal object carries one.\n *\n * Throws if the format string uses a token the input type doesn't support\n * (e.g. 'HH' on a PlainDate) rather than silently printing \"undefined\".\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: FormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = tokenize(formatStr);\n let result = '';\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n if (!handler) {\n // shouldn't happen — tokenize() only emits tokens from TOKENS\n throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n }\n\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`\n );\n }\n\n result += handler.fn(temporal, locale);\n }\n\n return result;\n}\n","// Closed-vocabulary lookups for locale-aware tokens (MMMM, MMM, EEEE, EEE, a).\n// Each set is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nexport interface LocaleVocab {\n monthLong: string[]; // index 0 = January\n monthShort: string[];\n weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n weekdayShort: string[];\n dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\n\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const part = formatter.formatToParts(date).find((p) => p.type === type);\n if (!part) {\n throw new Error(`temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`);\n }\n return part.value;\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n const cached = vocabCache.get(locale);\n if (cached) {\n return cached;\n }\n\n const monthLongFmt = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const monthLong: string[] = [];\n const monthShort: string[] = [];\n for (let m = 0; m < 12; m++) {\n const date = new Date(Date.UTC(2020, m, 1));\n monthLong.push(partValue(monthLongFmt, date, 'month'));\n monthShort.push(partValue(monthShortFmt, date, 'month'));\n }\n\n const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayLong: string[] = [];\n const weekdayShort: string[] = [];\n // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n for (let d = 0; d < 7; d++) {\n const date = new Date(Date.UTC(2024, 0, 1 + d));\n weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n }\n\n const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n const dayPeriod = [...new Set([am, pm])];\n\n const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n vocabCache.set(locale, vocab);\n return vocab;\n}\n","import type { Piece } from './tokenize.js';\nimport { getLocaleVocab } from './localeVocab.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[]): string {\n return `(?:${values.map(escapeRegExp).join('|')})`;\n}\n\nlet timeZoneFragment: string | undefined;\n\nfunction getTimeZoneFragment(): string {\n if (timeZoneFragment) {\n return timeZoneFragment;\n }\n const supportedValuesOf = (Intl as unknown as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf;\n if (typeof supportedValuesOf === 'function') {\n // supportedValuesOf('timeZone') leaves out 'UTC', but format() can\n // produce it from a real ZonedDateTime — without this, matchesFormat\n // rejected our own library's own output.\n timeZoneFragment = alternation([...supportedValuesOf('timeZone'), 'UTC']);\n } else {\n // no Intl.supportedValuesOf — match on shape only\n timeZoneFragment = '[A-Za-z_]+(?:\\\\/[A-Za-z_+\\\\-0-9]+)+|UTC';\n }\n return timeZoneFragment;\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yyyy: '\\\\d{4}',\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:[1-9]|1[0-2])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[1-9]|[12]\\\\d|3[01])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:[0-9]|1\\\\d|2[0-3])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:[1-9]|1[0-2])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[0-9]|[1-5]\\\\d)',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[0-9]|[1-5]\\\\d)',\n SSS: '\\\\d{3}',\n};\n\nfunction tokenFragment(token: string, locale: string): string {\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'MMMM': return alternation(vocab.monthLong);\n case 'MMM': return alternation(vocab.monthShort);\n case 'EEEE': return alternation(vocab.weekdayLong);\n case 'EEE': return alternation(vocab.weekdayShort);\n case 'a': return alternation(vocab.dayPeriod);\n case 'zzz': return getTimeZoneFragment();\n default:\n throw new Error(`temporal-fmt: unknown token \"${token}\"`);\n }\n}\n\n// Builds one anchored regex from tokenize() output — matches exactly what\n// format() could have produced for this format string, in this locale.\nexport function buildPatternSource(pieces: Piece[], locale: string): string {\n let source = '';\n for (const piece of pieces) {\n source += piece.kind === 'literal' ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);\n }\n return `^(?:${source})$`;\n}\n","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildPatternSource } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst patternCache = new Map<string, RegExp>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getPattern(formatStr: string, locale: string): RegExp {\n // \\0 can't appear in a locale tag or format string, so it's a safe join char\n const key = locale + '\\0' + formatStr;\n let pattern = patternCache.get(key);\n if (pattern) {\n return pattern;\n }\n if (patternCache.size >= MAX_CACHE_SIZE) {\n const oldestKey = patternCache.keys().next().value;\n if (oldestKey !== undefined) patternCache.delete(oldestKey);\n }\n const source = buildPatternSource(tokenize(formatStr), locale);\n pattern = new RegExp(source, 'u');\n patternCache.set(key, pattern);\n return pattern;\n}\n\n/**\n * Checks if `input` could plausibly be format()'s output for this format\n * string. Shape and vocabulary only — no parsing, and Feb 30 still passes.\n *\n * @example\n * matchesFormat('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // true\n * matchesFormat('yyyy-MM', '2026-08-04T15:45:30') // false\n */\nexport function matchesFormat(formatStr: string, input: string, options: FormatOptions = {}): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n return getPattern(formatStr, locale).test(input);\n}\n"],"mappings":"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","names":["index_exports","__export","format","matchesFormat","__toCommonJS","pad","n","len","DEFAULT_LOCALE","formatterCache","MAX_CACHE_SIZE","getFormatter","locale","options","key","formatter","oldestKey","intlPart","temporal","partType","toInstant","timeZoneId","isZoned","intlSafeTemporal","calendar","formatterOptions","part","p","TOKENS","SORTED_TOKEN_STRINGS","TOKENS","tok","a","b","tokenize","format","pieces","i","ch","appendLiteral","j","literal","closed","match","value","last","HANDLER_BY_TOKEN","TOKENS","tok","fn","field","format","temporal","formatStr","options","locale","DEFAULT_LOCALE","pieces","tokenize","result","piece","handler","vocabCache","partValue","formatter","date","type","part","p","getLocaleVocab","locale","cached","monthLongFmt","monthShortFmt","monthLong","monthShort","m","weekdayLongFmt","weekdayShortFmt","weekdayLong","weekdayShort","d","dayPeriodFmt","am","pm","dayPeriod","vocab","escapeRegExp","literal","alternation","values","timeZoneFragment","getTimeZoneFragment","supportedValuesOf","NUMERIC_FRAGMENTS","tokenFragment","token","locale","numeric","vocab","getLocaleVocab","buildPatternSource","pieces","source","piece","patternCache","MAX_CACHE_SIZE","getPattern","formatStr","locale","key","pattern","oldestKey","source","buildPatternSource","tokenize","matchesFormat","input","options","DEFAULT_LOCALE"]}
|
package/dist/index.js
CHANGED
|
@@ -1,305 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function pad(n, len) {
|
|
3
|
-
return String(n).padStart(len, "0");
|
|
4
|
-
}
|
|
5
|
-
var DEFAULT_LOCALE = "en-US";
|
|
6
|
-
var formatterCache = /* @__PURE__ */ new Map();
|
|
7
|
-
var MAX_CACHE_SIZE = 500;
|
|
8
|
-
function getFormatter(locale, options) {
|
|
9
|
-
const key = locale + JSON.stringify(options);
|
|
10
|
-
let formatter = formatterCache.get(key);
|
|
11
|
-
if (formatter) {
|
|
12
|
-
return formatter;
|
|
13
|
-
}
|
|
14
|
-
if (formatterCache.size >= MAX_CACHE_SIZE) {
|
|
15
|
-
const oldestKey = formatterCache.keys().next().value;
|
|
16
|
-
if (oldestKey !== void 0) formatterCache.delete(oldestKey);
|
|
17
|
-
}
|
|
18
|
-
formatter = new Intl.DateTimeFormat(locale, options);
|
|
19
|
-
formatterCache.set(key, formatter);
|
|
20
|
-
return formatter;
|
|
21
|
-
}
|
|
22
|
-
function intlPart(temporal, locale, options, partType) {
|
|
23
|
-
const { toInstant, timeZoneId } = temporal;
|
|
24
|
-
const isZoned = typeof toInstant === "function" && typeof timeZoneId === "string";
|
|
25
|
-
const intlSafeTemporal = isZoned ? temporal.toInstant() : temporal;
|
|
26
|
-
const calendar = temporal?.calendarId;
|
|
27
|
-
const formatterOptions = {
|
|
28
|
-
...options,
|
|
29
|
-
...calendar && calendar !== "iso8601" ? { calendar } : {},
|
|
30
|
-
...isZoned ? { timeZone: timeZoneId } : {}
|
|
31
|
-
};
|
|
32
|
-
const formatter = getFormatter(locale, formatterOptions);
|
|
33
|
-
const parts = formatter.formatToParts(intlSafeTemporal);
|
|
34
|
-
const part = parts.find((p) => p.type === partType);
|
|
35
|
-
if (!part) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`temporal-fmt: locale "${locale}" produced no "${partType}" part for this token. This usually means the Temporal object is missing the field the token needs.`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return part.value;
|
|
41
|
-
}
|
|
42
|
-
var TOKENS = [
|
|
43
|
-
["yyyy", (t) => pad(t.year, 4), "year"],
|
|
44
|
-
["yy", (t) => {
|
|
45
|
-
if (t.year < 0) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
`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.`
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
return pad(t.year % 100, 2);
|
|
51
|
-
}, "year"],
|
|
52
|
-
["MMMM", (t, locale) => intlPart(t, locale, { month: "long" }, "month"), "month"],
|
|
53
|
-
["MMM", (t, locale) => intlPart(t, locale, { month: "short" }, "month"), "month"],
|
|
54
|
-
["MM", (t) => pad(t.month, 2), "month"],
|
|
55
|
-
["M", (t) => String(t.month), "month"],
|
|
56
|
-
["dd", (t) => pad(t.day, 2), "day"],
|
|
57
|
-
["d", (t) => String(t.day), "day"],
|
|
58
|
-
["EEEE", (t, locale) => intlPart(t, locale, { weekday: "long" }, "weekday"), "dayOfWeek"],
|
|
59
|
-
["EEE", (t, locale) => intlPart(t, locale, { weekday: "short" }, "weekday"), "dayOfWeek"],
|
|
60
|
-
["HH", (t) => pad(t.hour, 2), "hour"],
|
|
61
|
-
["H", (t) => String(t.hour), "hour"],
|
|
62
|
-
["hh", (t) => pad(t.hour % 12 || 12, 2), "hour"],
|
|
63
|
-
["h", (t) => String(t.hour % 12 || 12), "hour"],
|
|
64
|
-
["mm", (t) => pad(t.minute, 2), "minute"],
|
|
65
|
-
["m", (t) => String(t.minute), "minute"],
|
|
66
|
-
["ss", (t) => pad(t.second, 2), "second"],
|
|
67
|
-
["s", (t) => String(t.second), "second"],
|
|
68
|
-
["SSS", (t) => pad(t.millisecond, 3), "millisecond"],
|
|
69
|
-
// dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but
|
|
70
|
-
// still needs .hour on the input to compute which period it is
|
|
71
|
-
["a", (t, locale) => intlPart(t, locale, { hour: "numeric", hour12: true }, "dayPeriod"), "hour"],
|
|
72
|
-
["zzz", (t) => t.timeZoneId, "timeZoneId"]
|
|
73
|
-
];
|
|
74
|
-
|
|
75
|
-
// src/tokenize.ts
|
|
76
|
-
var SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);
|
|
77
|
-
function tokenize(format2) {
|
|
78
|
-
const pieces = [];
|
|
79
|
-
let i = 0;
|
|
80
|
-
while (i < format2.length) {
|
|
81
|
-
const ch = format2[i];
|
|
82
|
-
if (ch === "'") {
|
|
83
|
-
if (format2[i + 1] === "'") {
|
|
84
|
-
appendLiteral(pieces, "'");
|
|
85
|
-
i += 2;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
let j = i + 1;
|
|
89
|
-
let literal = "";
|
|
90
|
-
let closed = false;
|
|
91
|
-
while (j < format2.length) {
|
|
92
|
-
if (format2[j] === "'") {
|
|
93
|
-
if (format2[j + 1] === "'") {
|
|
94
|
-
literal += "'";
|
|
95
|
-
j += 2;
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
closed = true;
|
|
99
|
-
j += 1;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
literal += format2[j];
|
|
103
|
-
j += 1;
|
|
104
|
-
}
|
|
105
|
-
if (!closed) {
|
|
106
|
-
throw new Error(`temporal-fmt: unterminated quote in format string "${format2}"`);
|
|
107
|
-
}
|
|
108
|
-
appendLiteral(pieces, literal);
|
|
109
|
-
i = j;
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
const match = SORTED_TOKEN_STRINGS.find((tok) => format2.startsWith(tok, i));
|
|
113
|
-
if (match) {
|
|
114
|
-
pieces.push({ kind: "token", value: match });
|
|
115
|
-
i += match.length;
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
appendLiteral(pieces, ch);
|
|
119
|
-
i += 1;
|
|
120
|
-
}
|
|
121
|
-
return pieces;
|
|
122
|
-
}
|
|
123
|
-
function appendLiteral(pieces, value) {
|
|
124
|
-
const last = pieces[pieces.length - 1];
|
|
125
|
-
if (last && last.kind === "literal") {
|
|
126
|
-
last.value += value;
|
|
127
|
-
} else {
|
|
128
|
-
pieces.push({ kind: "literal", value });
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// src/constants.ts
|
|
133
|
-
var MAX_FORMAT_LENGTH = 1e3;
|
|
134
|
-
|
|
135
|
-
// src/format.ts
|
|
136
|
-
var HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));
|
|
137
|
-
function format(temporal, formatStr, options = {}) {
|
|
138
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
144
|
-
const pieces = tokenize(formatStr);
|
|
145
|
-
let result = "";
|
|
146
|
-
for (const piece of pieces) {
|
|
147
|
-
if (piece.kind === "literal") {
|
|
148
|
-
result += piece.value;
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const handler = HANDLER_BY_TOKEN.get(piece.value);
|
|
152
|
-
if (!handler) {
|
|
153
|
-
throw new Error(`temporal-fmt: unknown token "${piece.value}"`);
|
|
154
|
-
}
|
|
155
|
-
if (temporal[handler.field] === void 0) {
|
|
156
|
-
throw new Error(
|
|
157
|
-
`temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
result += handler.fn(temporal, locale);
|
|
161
|
-
}
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// src/localeVocab.ts
|
|
166
|
-
var vocabCache = /* @__PURE__ */ new Map();
|
|
167
|
-
function partValue(formatter, date, type) {
|
|
168
|
-
const part = formatter.formatToParts(date).find((p) => p.type === type);
|
|
169
|
-
if (!part) {
|
|
170
|
-
throw new Error(`temporal-fmt: locale produced no "${type}" part while building match vocabulary.`);
|
|
171
|
-
}
|
|
172
|
-
return part.value;
|
|
173
|
-
}
|
|
174
|
-
function getLocaleVocab(locale) {
|
|
175
|
-
const cached = vocabCache.get(locale);
|
|
176
|
-
if (cached) {
|
|
177
|
-
return cached;
|
|
178
|
-
}
|
|
179
|
-
const monthLongFmt = new Intl.DateTimeFormat(locale, { month: "long", timeZone: "UTC" });
|
|
180
|
-
const monthShortFmt = new Intl.DateTimeFormat(locale, { month: "short", timeZone: "UTC" });
|
|
181
|
-
const monthLong = [];
|
|
182
|
-
const monthShort = [];
|
|
183
|
-
for (let m = 0; m < 12; m++) {
|
|
184
|
-
const date = new Date(Date.UTC(2020, m, 1));
|
|
185
|
-
monthLong.push(partValue(monthLongFmt, date, "month"));
|
|
186
|
-
monthShort.push(partValue(monthShortFmt, date, "month"));
|
|
187
|
-
}
|
|
188
|
-
const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: "long", timeZone: "UTC" });
|
|
189
|
-
const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
|
|
190
|
-
const weekdayLong = [];
|
|
191
|
-
const weekdayShort = [];
|
|
192
|
-
for (let d = 0; d < 7; d++) {
|
|
193
|
-
const date = new Date(Date.UTC(2024, 0, 1 + d));
|
|
194
|
-
weekdayLong.push(partValue(weekdayLongFmt, date, "weekday"));
|
|
195
|
-
weekdayShort.push(partValue(weekdayShortFmt, date, "weekday"));
|
|
196
|
-
}
|
|
197
|
-
const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: "numeric", hour12: true, timeZone: "UTC" });
|
|
198
|
-
const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), "dayPeriod");
|
|
199
|
-
const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), "dayPeriod");
|
|
200
|
-
const dayPeriod = [.../* @__PURE__ */ new Set([am, pm])];
|
|
201
|
-
const vocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };
|
|
202
|
-
vocabCache.set(locale, vocab);
|
|
203
|
-
return vocab;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// src/pattern.ts
|
|
207
|
-
function escapeRegExp(literal) {
|
|
208
|
-
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
209
|
-
}
|
|
210
|
-
function alternation(values) {
|
|
211
|
-
return `(?:${values.map(escapeRegExp).join("|")})`;
|
|
212
|
-
}
|
|
213
|
-
var timeZoneFragment;
|
|
214
|
-
function getTimeZoneFragment() {
|
|
215
|
-
if (timeZoneFragment) {
|
|
216
|
-
return timeZoneFragment;
|
|
217
|
-
}
|
|
218
|
-
const supportedValuesOf = Intl.supportedValuesOf;
|
|
219
|
-
if (typeof supportedValuesOf === "function") {
|
|
220
|
-
timeZoneFragment = alternation([...supportedValuesOf("timeZone"), "UTC"]);
|
|
221
|
-
} else {
|
|
222
|
-
timeZoneFragment = "[A-Za-z_]+(?:\\/[A-Za-z_+\\-0-9]+)+|UTC";
|
|
223
|
-
}
|
|
224
|
-
return timeZoneFragment;
|
|
225
|
-
}
|
|
226
|
-
var NUMERIC_FRAGMENTS = {
|
|
227
|
-
yyyy: "\\d{4}",
|
|
228
|
-
yy: "\\d{2}",
|
|
229
|
-
MM: "(?:0[1-9]|1[0-2])",
|
|
230
|
-
M: "(?:[1-9]|1[0-2])",
|
|
231
|
-
dd: "(?:0[1-9]|[12]\\d|3[01])",
|
|
232
|
-
d: "(?:[1-9]|[12]\\d|3[01])",
|
|
233
|
-
HH: "(?:[01]\\d|2[0-3])",
|
|
234
|
-
H: "(?:[0-9]|1\\d|2[0-3])",
|
|
235
|
-
hh: "(?:0[1-9]|1[0-2])",
|
|
236
|
-
h: "(?:[1-9]|1[0-2])",
|
|
237
|
-
mm: "(?:[0-5]\\d)",
|
|
238
|
-
m: "(?:[0-9]|[1-5]\\d)",
|
|
239
|
-
ss: "(?:[0-5]\\d)",
|
|
240
|
-
s: "(?:[0-9]|[1-5]\\d)",
|
|
241
|
-
SSS: "\\d{3}"
|
|
242
|
-
};
|
|
243
|
-
function tokenFragment(token, locale) {
|
|
244
|
-
const numeric = NUMERIC_FRAGMENTS[token];
|
|
245
|
-
if (numeric) {
|
|
246
|
-
return numeric;
|
|
247
|
-
}
|
|
248
|
-
const vocab = getLocaleVocab(locale);
|
|
249
|
-
switch (token) {
|
|
250
|
-
case "MMMM":
|
|
251
|
-
return alternation(vocab.monthLong);
|
|
252
|
-
case "MMM":
|
|
253
|
-
return alternation(vocab.monthShort);
|
|
254
|
-
case "EEEE":
|
|
255
|
-
return alternation(vocab.weekdayLong);
|
|
256
|
-
case "EEE":
|
|
257
|
-
return alternation(vocab.weekdayShort);
|
|
258
|
-
case "a":
|
|
259
|
-
return alternation(vocab.dayPeriod);
|
|
260
|
-
case "zzz":
|
|
261
|
-
return getTimeZoneFragment();
|
|
262
|
-
default:
|
|
263
|
-
throw new Error(`temporal-fmt: unknown token "${token}"`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function buildPatternSource(pieces, locale) {
|
|
267
|
-
let source = "";
|
|
268
|
-
for (const piece of pieces) {
|
|
269
|
-
source += piece.kind === "literal" ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);
|
|
270
|
-
}
|
|
271
|
-
return `^(?:${source})$`;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// src/matchesFormat.ts
|
|
275
|
-
var patternCache = /* @__PURE__ */ new Map();
|
|
276
|
-
var MAX_CACHE_SIZE2 = 500;
|
|
277
|
-
function getPattern(formatStr, locale) {
|
|
278
|
-
const key = locale + "\0" + formatStr;
|
|
279
|
-
let pattern = patternCache.get(key);
|
|
280
|
-
if (pattern) {
|
|
281
|
-
return pattern;
|
|
282
|
-
}
|
|
283
|
-
if (patternCache.size >= MAX_CACHE_SIZE2) {
|
|
284
|
-
const oldestKey = patternCache.keys().next().value;
|
|
285
|
-
if (oldestKey !== void 0) patternCache.delete(oldestKey);
|
|
286
|
-
}
|
|
287
|
-
const source = buildPatternSource(tokenize(formatStr), locale);
|
|
288
|
-
pattern = new RegExp(source, "u");
|
|
289
|
-
patternCache.set(key, pattern);
|
|
290
|
-
return pattern;
|
|
291
|
-
}
|
|
292
|
-
function matchesFormat(formatStr, input, options = {}) {
|
|
293
|
-
if (formatStr.length > MAX_FORMAT_LENGTH) {
|
|
294
|
-
throw new Error(
|
|
295
|
-
`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
const locale = options.locale ?? DEFAULT_LOCALE;
|
|
299
|
-
return getPattern(formatStr, locale).test(input);
|
|
300
|
-
}
|
|
301
|
-
export {
|
|
302
|
-
format,
|
|
303
|
-
matchesFormat
|
|
304
|
-
};
|
|
1
|
+
function m(t,e){return String(t).padStart(e,"0")}var T="en-US",h=new Map,v=500;function A(t,e){let n=t+JSON.stringify(e),o=h.get(n);if(o)return o;if(h.size>=v){let i=h.keys().next().value;i!==void 0&&h.delete(i)}return o=new Intl.DateTimeFormat(t,e),h.set(n,o),o}function p(t,e,n,o){let{toInstant:i,timeZoneId:r}=t,a=typeof i=="function"&&typeof r=="string",s=a?t.toInstant():t,u=t?.calendarId,w={...n,...u&&u!=="iso8601"?{calendar:u}:{},...a?{timeZone:r}:{}},k=A(e,w).formatToParts(s).find(M=>M.type===o);if(!k)throw new Error(`temporal-fmt: locale "${e}" produced no "${o}" part for this token. This usually means the Temporal object is missing the field the token needs.`);return k.value}var E=[["yyyy",t=>m(t.year,4),"year"],["yy",t=>{if(t.year<0)throw new Error(`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.`);return m(t.year%100,2)},"year"],["MMMM",(t,e)=>p(t,e,{month:"long"},"month"),"month"],["MMM",(t,e)=>p(t,e,{month:"short"},"month"),"month"],["MM",t=>m(t.month,2),"month"],["M",t=>String(t.month),"month"],["dd",t=>m(t.day,2),"day"],["d",t=>String(t.day),"day"],["EEEE",(t,e)=>p(t,e,{weekday:"long"},"weekday"),"dayOfWeek"],["EEE",(t,e)=>p(t,e,{weekday:"short"},"weekday"),"dayOfWeek"],["HH",t=>m(t.hour,2),"hour"],["H",t=>String(t.hour),"hour"],["hh",t=>m(t.hour%12||12,2),"hour"],["h",t=>String(t.hour%12||12),"hour"],["mm",t=>m(t.minute,2),"minute"],["m",t=>String(t.minute),"minute"],["ss",t=>m(t.second,2),"second"],["s",t=>String(t.second),"second"],["SSS",t=>m(t.millisecond,3),"millisecond"],["a",(t,e)=>p(t,e,{hour:"numeric",hour12:!0},"dayPeriod"),"hour"],["zzz",t=>t.timeZoneId,"timeZoneId"]];var Z=E.map(([t])=>t).sort((t,e)=>e.length-t.length);function F(t){let e=[],n=0;for(;n<t.length;){let o=t[n];if(o==="'"){if(t[n+1]==="'"){S(e,"'"),n+=2;continue}let r=n+1,a="",s=!1;for(;r<t.length;){if(t[r]==="'"){if(t[r+1]==="'"){a+="'",r+=2;continue}s=!0,r+=1;break}a+=t[r],r+=1}if(!s)throw new Error(`temporal-fmt: unterminated quote in format string "${t}"`);S(e,a),n=r;continue}let i=Z.find(r=>t.startsWith(r,n));if(i){e.push({kind:"token",value:i}),n+=i.length;continue}S(e,o),n+=1}return e}function S(t,e){let n=t[t.length-1];n&&n.kind==="literal"?n.value+=e:t.push({kind:"literal",value:e})}var _=new Map(E.map(([t,e,n])=>[t,{fn:e,field:n}]));function $(t,e,n={}){if(e.length>1e3)throw new Error(`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`);let o=n.locale??T,i=F(e),r="";for(let a of i){if(a.kind==="literal"){r+=a.value;continue}let s=_.get(a.value);if(!s)throw new Error(`temporal-fmt: unknown token "${a.value}"`);if(t[s.field]===void 0)throw new Error(`temporal-fmt: token "${a.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`);r+=s.fn(t,o)}return r}var O=new Map;function l(t,e,n){let o=t.formatToParts(e).find(i=>i.type===n);if(!o)throw new Error(`temporal-fmt: locale produced no "${n}" part while building match vocabulary.`);return o.value}function x(t){let e=O.get(t);if(e)return e;let n=new Intl.DateTimeFormat(t,{month:"long",timeZone:"UTC"}),o=new Intl.DateTimeFormat(t,{month:"short",timeZone:"UTC"}),i=[],r=[];for(let c=0;c<12;c++){let g=new Date(Date.UTC(2020,c,1));i.push(l(n,g,"month")),r.push(l(o,g,"month"))}let a=new Intl.DateTimeFormat(t,{weekday:"long",timeZone:"UTC"}),s=new Intl.DateTimeFormat(t,{weekday:"short",timeZone:"UTC"}),u=[],w=[];for(let c=0;c<7;c++){let g=new Date(Date.UTC(2024,0,1+c));u.push(l(a,g,"weekday")),w.push(l(s,g,"weekday"))}let L=new Intl.DateTimeFormat(t,{hour:"numeric",hour12:!0,timeZone:"UTC"}),b=l(L,new Date(Date.UTC(2020,0,1,1)),"dayPeriod"),k=l(L,new Date(Date.UTC(2020,0,1,13)),"dayPeriod"),M=[...new Set([b,k])],I={monthLong:i,monthShort:r,weekdayLong:u,weekdayShort:w,dayPeriod:M};return O.set(t,I),I}function P(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function d(t){return`(?:${t.map(P).join("|")})`}var f;function U(){if(f)return f;let t=Intl.supportedValuesOf;return typeof t=="function"?f=d([...t("timeZone"),"UTC"]):f="[A-Za-z_]+(?:\\/[A-Za-z_+\\-0-9]+)+|UTC",f}var z={yyyy:"\\d{4}",yy:"\\d{2}",MM:"(?:0[1-9]|1[0-2])",M:"(?:[1-9]|1[0-2])",dd:"(?:0[1-9]|[12]\\d|3[01])",d:"(?:[1-9]|[12]\\d|3[01])",HH:"(?:[01]\\d|2[0-3])",H:"(?:[0-9]|1\\d|2[0-3])",hh:"(?:0[1-9]|1[0-2])",h:"(?:[1-9]|1[0-2])",mm:"(?:[0-5]\\d)",m:"(?:[0-9]|[1-5]\\d)",ss:"(?:[0-5]\\d)",s:"(?:[0-9]|[1-5]\\d)",SSS:"\\d{3}"};function H(t,e){let n=z[t];if(n)return n;let o=x(e);switch(t){case"MMMM":return d(o.monthLong);case"MMM":return d(o.monthShort);case"EEEE":return d(o.weekdayLong);case"EEE":return d(o.weekdayShort);case"a":return d(o.dayPeriod);case"zzz":return U();default:throw new Error(`temporal-fmt: unknown token "${t}"`)}}function C(t,e){let n="";for(let o of t)n+=o.kind==="literal"?P(o.value):H(o.value,e);return`^(?:${n})$`}var y=new Map,N=500;function R(t,e){let n=e+"\0"+t,o=y.get(n);if(o)return o;if(y.size>=N){let r=y.keys().next().value;r!==void 0&&y.delete(r)}let i=C(F(t),e);return o=new RegExp(i,"u"),y.set(n,o),o}function V(t,e,n={}){if(t.length>1e3)throw new Error(`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${t.length}).`);let o=n.locale??T;return R(t,o).test(e)}export{$ as format,V as matchesFormat};
|
|
305
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tokens.ts","../src/tokenize.ts","../src/constants.ts","../src/format.ts","../src/localeVocab.ts","../src/pattern.ts","../src/matchesFormat.ts"],"sourcesContent":["export function pad(n: number, len: number): string {\n return String(n).padStart(len, '0');\n}\n\n// Not every field exists on every Temporal type (PlainDate has no .hour,\n// etc). Callers check for undefined before formatting a token.\nexport interface TemporalLike {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n timeZoneId?: string;\n dayOfWeek?: number; // 1 (Mon) - 7 (Sun), per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n}\n\nexport interface FormatOptions {\n /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */\n locale?: string;\n}\n\nexport const DEFAULT_LOCALE = 'en-US';\n\n// Intl.DateTimeFormat is expensive to construct and format() can run in a\n// loop (rendering a table of dates), so cache by (locale, options).\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + JSON.stringify(options);\n let formatter = formatterCache.get(key);\n if (formatter) {\n return formatter;\n }\n if (formatterCache.size >= MAX_CACHE_SIZE) {\n // not real LRU, just evicts oldest insertion — fine for this key space\n const oldestKey = formatterCache.keys().next().value;\n if (oldestKey !== undefined) formatterCache.delete(oldestKey);\n }\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Pulls a single field out of formatToParts() rather than building a full\n// string and slicing it — slicing breaks under RTL and locales with\n// different field ordering.\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\n // formatToParts() throws on ZonedDateTime directly (per spec), so convert\n // to Instant and pass the zone via `timeZone` instead. Don't convert to\n // PlainDateTime — that drops the zone, which breaks 'MMMM' + 'zzz' combos.\n const { toInstant, timeZoneId } = temporal;\n const isZoned = typeof toInstant === 'function' && typeof timeZoneId === 'string';\n // has to be called as temporal.toInstant() — destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n\n // Intl throws \"Mismatching Calendars\" if the formatter's calendar doesn't\n // match the object's own (e.g. en-US formatter defaults to gregory, but\n // a hebrew/islamic PlainDate needs its own calendar passed through).\n //\n // skip this for iso8601 specifically — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes formatToParts()\n // come back empty. no idea why, cost me an hour.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n ...(calendar && calendar !== 'iso8601' ? { calendar } : {}),\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, formatterOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const part = parts.find((p) => p.type === partType);\n if (!part) {\n throw new Error(\n `temporal-fmt: locale \"${locale}\" produced no \"${partType}\" part for this token. ` +\n `This usually means the Temporal object is missing the field the token needs.`\n );\n }\n return part.value;\n}\n\ntype TokenHandler = (t: TemporalLike, locale: string) => string;\n\n// Longest-first — tokenizer is greedy, \"yyyy\" has to be tried before \"yy\".\n//\n// Numeric tokens always render in ASCII digits, never locale-native\n// (Arabic-Indic, Devanagari, etc). Padding non-ASCII digit strings to a\n// fixed width isn't the same operation as padding \"3\", and most consumers\n// parsing these back out want plain digits anyway.\nexport const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]> = [\n ['yyyy', (t) => pad(t.year!, 4), 'year'],\n ['yy', (t) => {\n // -45 % 100 === -45, so truncating negative years to 2 digits doesn't\n // work and Math.abs() would make 45 CE and 45 BCE render the same.\n if (t.year! < 0) {\n throw new Error(\n `temporal-fmt: token \"yy\" doesn't support negative years (got ${t.year}), ` +\n `since truncating to 2 digits would make it indistinguishable from a ` +\n `positive year. Use \"yyyy\" instead.`\n );\n }\n return pad(t.year! % 100, 2);\n }, 'year'],\n ['MMMM', (t, locale) => intlPart(t, locale, { month: 'long' }, 'month'), 'month'],\n ['MMM', (t, locale) => intlPart(t, locale, { month: 'short' }, 'month'), 'month'],\n ['MM', (t) => pad(t.month!, 2), 'month'],\n ['M', (t) => String(t.month!), 'month'],\n ['dd', (t) => pad(t.day!, 2), 'day'],\n ['d', (t) => String(t.day!), 'day'],\n ['EEEE', (t, locale) => intlPart(t, locale, { weekday: 'long' }, 'weekday'), 'dayOfWeek'],\n ['EEE', (t, locale) => intlPart(t, locale, { weekday: 'short' }, 'weekday'), 'dayOfWeek'],\n ['HH', (t) => pad(t.hour!, 2), 'hour'],\n ['H', (t) => String(t.hour!), 'hour'],\n ['hh', (t) => pad(t.hour! % 12 || 12, 2), 'hour'],\n ['h', (t) => String(t.hour! % 12 || 12), 'hour'],\n ['mm', (t) => pad(t.minute!, 2), 'minute'],\n ['m', (t) => String(t.minute!), 'minute'],\n ['ss', (t) => pad(t.second!, 2), 'second'],\n ['s', (t) => String(t.second!), 'second'],\n ['SSS', (t) => pad(t.millisecond!, 3), 'millisecond'],\n // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but\n // still needs .hour on the input to compute which period it is\n ['a', (t, locale) => intlPart(t, locale, { hour: 'numeric', hour12: true }, 'dayPeriod'), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n];\n","import { TOKENS } from './tokens.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in format string \"${format}\"`);\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is \n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}\n","// format strings are short hand-written literals (\"yyyy-MM-dd\")\n// cap the length so a bug or bad input can't make tokenize() do unbounded work\nexport const MAX_FORMAT_LENGTH = 1000;","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n/**\n * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime\n * using a date-fns-style token string.\n *\n * @example\n * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // \"2026-08-04\"\n * format(zdt, \"MMM d, yyyy 'at' h:mm a\") // \"Aug 4, 2026 at 3:45 PM\"\n * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // \"août 4, 2026\"\n * format(zdt, 'EEEE d MMMM', { locale: 'ar-EG' }) // Arabic weekday/month names\n *\n * Numeric fields always render in ASCII digits regardless of locale.\n * Named fields (MMMM, EEEE, a) are fully localized via Intl, including\n * non-Gregorian calendars if the Temporal object carries one.\n *\n * Throws if the format string uses a token the input type doesn't support\n * (e.g. 'HH' on a PlainDate) rather than silently printing \"undefined\".\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: FormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = tokenize(formatStr);\n let result = '';\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n if (!handler) {\n // shouldn't happen — tokenize() only emits tokens from TOKENS\n throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n }\n\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`\n );\n }\n\n result += handler.fn(temporal, locale);\n }\n\n return result;\n}\n","// Closed-vocabulary lookups for locale-aware tokens (MMMM, MMM, EEEE, EEE, a).\n// Each set is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nexport interface LocaleVocab {\n monthLong: string[]; // index 0 = January\n monthShort: string[];\n weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n weekdayShort: string[];\n dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\n\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const part = formatter.formatToParts(date).find((p) => p.type === type);\n if (!part) {\n throw new Error(`temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`);\n }\n return part.value;\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n const cached = vocabCache.get(locale);\n if (cached) {\n return cached;\n }\n\n const monthLongFmt = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const monthLong: string[] = [];\n const monthShort: string[] = [];\n for (let m = 0; m < 12; m++) {\n const date = new Date(Date.UTC(2020, m, 1));\n monthLong.push(partValue(monthLongFmt, date, 'month'));\n monthShort.push(partValue(monthShortFmt, date, 'month'));\n }\n\n const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayLong: string[] = [];\n const weekdayShort: string[] = [];\n // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n for (let d = 0; d < 7; d++) {\n const date = new Date(Date.UTC(2024, 0, 1 + d));\n weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n }\n\n const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n const dayPeriod = [...new Set([am, pm])];\n\n const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n vocabCache.set(locale, vocab);\n return vocab;\n}\n","import type { Piece } from './tokenize.js';\nimport { getLocaleVocab } from './localeVocab.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[]): string {\n return `(?:${values.map(escapeRegExp).join('|')})`;\n}\n\nlet timeZoneFragment: string | undefined;\n\nfunction getTimeZoneFragment(): string {\n if (timeZoneFragment) {\n return timeZoneFragment;\n }\n const supportedValuesOf = (Intl as unknown as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf;\n if (typeof supportedValuesOf === 'function') {\n // supportedValuesOf('timeZone') leaves out 'UTC', but format() can\n // produce it from a real ZonedDateTime — without this, matchesFormat\n // rejected our own library's own output.\n timeZoneFragment = alternation([...supportedValuesOf('timeZone'), 'UTC']);\n } else {\n // no Intl.supportedValuesOf — match on shape only\n timeZoneFragment = '[A-Za-z_]+(?:\\\\/[A-Za-z_+\\\\-0-9]+)+|UTC';\n }\n return timeZoneFragment;\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yyyy: '\\\\d{4}',\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:[1-9]|1[0-2])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[1-9]|[12]\\\\d|3[01])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:[0-9]|1\\\\d|2[0-3])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:[1-9]|1[0-2])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[0-9]|[1-5]\\\\d)',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[0-9]|[1-5]\\\\d)',\n SSS: '\\\\d{3}',\n};\n\nfunction tokenFragment(token: string, locale: string): string {\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'MMMM': return alternation(vocab.monthLong);\n case 'MMM': return alternation(vocab.monthShort);\n case 'EEEE': return alternation(vocab.weekdayLong);\n case 'EEE': return alternation(vocab.weekdayShort);\n case 'a': return alternation(vocab.dayPeriod);\n case 'zzz': return getTimeZoneFragment();\n default:\n throw new Error(`temporal-fmt: unknown token \"${token}\"`);\n }\n}\n\n// Builds one anchored regex from tokenize() output — matches exactly what\n// format() could have produced for this format string, in this locale.\nexport function buildPatternSource(pieces: Piece[], locale: string): string {\n let source = '';\n for (const piece of pieces) {\n source += piece.kind === 'literal' ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);\n }\n return `^(?:${source})$`;\n}\n","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildPatternSource } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst patternCache = new Map<string, RegExp>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getPattern(formatStr: string, locale: string): RegExp {\n // \\0 can't appear in a locale tag or format string, so it's a safe join char\n const key = locale + '\\0' + formatStr;\n let pattern = patternCache.get(key);\n if (pattern) {\n return pattern;\n }\n if (patternCache.size >= MAX_CACHE_SIZE) {\n const oldestKey = patternCache.keys().next().value;\n if (oldestKey !== undefined) patternCache.delete(oldestKey);\n }\n const source = buildPatternSource(tokenize(formatStr), locale);\n pattern = new RegExp(source, 'u');\n patternCache.set(key, pattern);\n return pattern;\n}\n\n/**\n * Checks if `input` could plausibly be format()'s output for this format\n * string. Shape and vocabulary only — no parsing, and Feb 30 still passes.\n *\n * @example\n * matchesFormat('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // true\n * matchesFormat('yyyy-MM', '2026-08-04T15:45:30') // false\n */\nexport function matchesFormat(formatStr: string, input: string, options: FormatOptions = {}): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n return getPattern(formatStr, locale).test(input);\n}\n"],"mappings":"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","names":["format","MAX_CACHE_SIZE"]}
|
|
1
|
+
{"version":3,"sources":["../src/tokens.ts","../src/tokenize.ts","../src/format.ts","../src/localeVocab.ts","../src/pattern.ts","../src/matchesFormat.ts"],"sourcesContent":["export function pad(n: number, len: number): string {\n return String(n).padStart(len, '0');\n}\n\n// Not every field exists on every Temporal type (PlainDate has no .hour,\n// etc). Callers check for undefined before formatting a token.\nexport interface TemporalLike {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n timeZoneId?: string;\n dayOfWeek?: number; // 1 (Mon) - 7 (Sun), per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n}\n\nexport interface FormatOptions {\n /** BCP 47 locale tag, e.g. 'en-US', 'fr-FR', 'ar-EG'. Defaults to 'en-US'. */\n locale?: string;\n}\n\nexport const DEFAULT_LOCALE = 'en-US';\n\n// Intl.DateTimeFormat is expensive to construct and format() can run in a\n// loop (rendering a table of dates), so cache by (locale, options).\nconst formatterCache = new Map<string, Intl.DateTimeFormat>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + JSON.stringify(options);\n let formatter = formatterCache.get(key);\n if (formatter) {\n return formatter;\n }\n if (formatterCache.size >= MAX_CACHE_SIZE) {\n // not real LRU, just evicts oldest insertion — fine for this key space\n const oldestKey = formatterCache.keys().next().value;\n if (oldestKey !== undefined) formatterCache.delete(oldestKey);\n }\n formatter = new Intl.DateTimeFormat(locale, options);\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Pulls a single field out of formatToParts() rather than building a full\n// string and slicing it — slicing breaks under RTL and locales with\n// different field ordering.\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\n // formatToParts() throws on ZonedDateTime directly (per spec), so convert\n // to Instant and pass the zone via `timeZone` instead. Don't convert to\n // PlainDateTime — that drops the zone, which breaks 'MMMM' + 'zzz' combos.\n const { toInstant, timeZoneId } = temporal;\n const isZoned = typeof toInstant === 'function' && typeof timeZoneId === 'string';\n // has to be called as temporal.toInstant() — destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n\n // Intl throws \"Mismatching Calendars\" if the formatter's calendar doesn't\n // match the object's own (e.g. en-US formatter defaults to gregory, but\n // a hebrew/islamic PlainDate needs its own calendar passed through).\n //\n // skip this for iso8601 specifically — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes formatToParts()\n // come back empty. no idea why, cost me an hour.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n ...(calendar && calendar !== 'iso8601' ? { calendar } : {}),\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, formatterOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const part = parts.find((p) => p.type === partType);\n if (!part) {\n throw new Error(\n `temporal-fmt: locale \"${locale}\" produced no \"${partType}\" part for this token. ` +\n `This usually means the Temporal object is missing the field the token needs.`\n );\n }\n return part.value;\n}\n\ntype TokenHandler = (t: TemporalLike, locale: string) => string;\n\n// Longest-first — tokenizer is greedy, \"yyyy\" has to be tried before \"yy\".\n//\n// Numeric tokens always render in ASCII digits, never locale-native\n// (Arabic-Indic, Devanagari, etc). Padding non-ASCII digit strings to a\n// fixed width isn't the same operation as padding \"3\", and most consumers\n// parsing these back out want plain digits anyway.\nexport const TOKENS: Array<[string, TokenHandler, keyof TemporalLike]> = [\n ['yyyy', (t) => pad(t.year!, 4), 'year'],\n ['yy', (t) => {\n // -45 % 100 === -45, so truncating negative years to 2 digits doesn't\n // work and Math.abs() would make 45 CE and 45 BCE render the same.\n if (t.year! < 0) {\n throw new Error(\n `temporal-fmt: token \"yy\" doesn't support negative years (got ${t.year}), ` +\n `since truncating to 2 digits would make it indistinguishable from a ` +\n `positive year. Use \"yyyy\" instead.`\n );\n }\n return pad(t.year! % 100, 2);\n }, 'year'],\n ['MMMM', (t, locale) => intlPart(t, locale, { month: 'long' }, 'month'), 'month'],\n ['MMM', (t, locale) => intlPart(t, locale, { month: 'short' }, 'month'), 'month'],\n ['MM', (t) => pad(t.month!, 2), 'month'],\n ['M', (t) => String(t.month!), 'month'],\n ['dd', (t) => pad(t.day!, 2), 'day'],\n ['d', (t) => String(t.day!), 'day'],\n ['EEEE', (t, locale) => intlPart(t, locale, { weekday: 'long' }, 'weekday'), 'dayOfWeek'],\n ['EEE', (t, locale) => intlPart(t, locale, { weekday: 'short' }, 'weekday'), 'dayOfWeek'],\n ['HH', (t) => pad(t.hour!, 2), 'hour'],\n ['H', (t) => String(t.hour!), 'hour'],\n ['hh', (t) => pad(t.hour! % 12 || 12, 2), 'hour'],\n ['h', (t) => String(t.hour! % 12 || 12), 'hour'],\n ['mm', (t) => pad(t.minute!, 2), 'minute'],\n ['m', (t) => String(t.minute!), 'minute'],\n ['ss', (t) => pad(t.second!, 2), 'second'],\n ['s', (t) => String(t.second!), 'second'],\n ['SSS', (t) => pad(t.millisecond!, 3), 'millisecond'],\n // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but\n // still needs .hour on the input to compute which period it is\n ['a', (t, locale) => intlPart(t, locale, { hour: 'numeric', hour12: true }, 'dayPeriod'), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n];\n","import { TOKENS } from './tokens.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in format string \"${format}\"`);\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is \n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}\n","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n/**\n * Format a Temporal.PlainDate, PlainTime, PlainDateTime, or ZonedDateTime\n * using a date-fns-style token string.\n *\n * @example\n * format(Temporal.Now.plainDateISO(), 'yyyy-MM-dd') // \"2026-08-04\"\n * format(zdt, \"MMM d, yyyy 'at' h:mm a\") // \"Aug 4, 2026 at 3:45 PM\"\n * format(zdt, 'MMMM d, yyyy', { locale: 'fr-FR' }) // \"août 4, 2026\"\n * format(zdt, 'EEEE d MMMM', { locale: 'ar-EG' }) // Arabic weekday/month names\n *\n * Numeric fields always render in ASCII digits regardless of locale.\n * Named fields (MMMM, EEEE, a) are fully localized via Intl, including\n * non-Gregorian calendars if the Temporal object carries one.\n *\n * Throws if the format string uses a token the input type doesn't support\n * (e.g. 'HH' on a PlainDate) rather than silently printing \"undefined\".\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: FormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = tokenize(formatStr);\n let result = '';\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n if (!handler) {\n // shouldn't happen — tokenize() only emits tokens from TOKENS\n throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n }\n\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${handler.field}\", ` +\n `which this Temporal object doesn't have. ` +\n `(e.g. PlainDate has no time fields, PlainTime has no date fields)`\n );\n }\n\n result += handler.fn(temporal, locale);\n }\n\n return result;\n}\n","// Closed-vocabulary lookups for locale-aware tokens (MMMM, MMM, EEEE, EEE, a).\n// Each set is small and fixed (12 months, 7 weekdays, 2 day periods), so we\n// generate the real Intl strings for a locale once and cache them.\n\nexport interface LocaleVocab {\n monthLong: string[]; // index 0 = January\n monthShort: string[];\n weekdayLong: string[]; // index 0 = Monday, per Temporal's dayOfWeek numbering\n weekdayShort: string[];\n dayPeriod: string[]; // typically [AM-ish, PM-ish], deduped\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\n\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const part = formatter.formatToParts(date).find((p) => p.type === type);\n if (!part) {\n throw new Error(`temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`);\n }\n return part.value;\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n const cached = vocabCache.get(locale);\n if (cached) {\n return cached;\n }\n\n const monthLongFmt = new Intl.DateTimeFormat(locale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(locale, { month: 'short', timeZone: 'UTC' });\n const monthLong: string[] = [];\n const monthShort: string[] = [];\n for (let m = 0; m < 12; m++) {\n const date = new Date(Date.UTC(2020, m, 1));\n monthLong.push(partValue(monthLongFmt, date, 'month'));\n monthShort.push(partValue(monthShortFmt, date, 'month'));\n }\n\n const weekdayLongFmt = new Intl.DateTimeFormat(locale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });\n const weekdayLong: string[] = [];\n const weekdayShort: string[] = [];\n // 2024-01-01 is a Monday (UTC) — walk 7 days from there for weekday names\n for (let d = 0; d < 7; d++) {\n const date = new Date(Date.UTC(2024, 0, 1 + d));\n weekdayLong.push(partValue(weekdayLongFmt, date, 'weekday'));\n weekdayShort.push(partValue(weekdayShortFmt, date, 'weekday'));\n }\n\n const dayPeriodFmt = new Intl.DateTimeFormat(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const am = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 1)), 'dayPeriod');\n const pm = partValue(dayPeriodFmt, new Date(Date.UTC(2020, 0, 1, 13)), 'dayPeriod');\n const dayPeriod = [...new Set([am, pm])];\n\n const vocab: LocaleVocab = { monthLong, monthShort, weekdayLong, weekdayShort, dayPeriod };\n vocabCache.set(locale, vocab);\n return vocab;\n}\n","import type { Piece } from './tokenize.js';\nimport { getLocaleVocab } from './localeVocab.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[]): string {\n return `(?:${values.map(escapeRegExp).join('|')})`;\n}\n\nlet timeZoneFragment: string | undefined;\n\nfunction getTimeZoneFragment(): string {\n if (timeZoneFragment) {\n return timeZoneFragment;\n }\n const supportedValuesOf = (Intl as unknown as { supportedValuesOf?: (key: string) => string[] }).supportedValuesOf;\n if (typeof supportedValuesOf === 'function') {\n // supportedValuesOf('timeZone') leaves out 'UTC', but format() can\n // produce it from a real ZonedDateTime — without this, matchesFormat\n // rejected our own library's own output.\n timeZoneFragment = alternation([...supportedValuesOf('timeZone'), 'UTC']);\n } else {\n // no Intl.supportedValuesOf — match on shape only\n timeZoneFragment = '[A-Za-z_]+(?:\\\\/[A-Za-z_+\\\\-0-9]+)+|UTC';\n }\n return timeZoneFragment;\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yyyy: '\\\\d{4}',\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:[1-9]|1[0-2])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[1-9]|[12]\\\\d|3[01])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:[0-9]|1\\\\d|2[0-3])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:[1-9]|1[0-2])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[0-9]|[1-5]\\\\d)',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[0-9]|[1-5]\\\\d)',\n SSS: '\\\\d{3}',\n};\n\nfunction tokenFragment(token: string, locale: string): string {\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'MMMM': return alternation(vocab.monthLong);\n case 'MMM': return alternation(vocab.monthShort);\n case 'EEEE': return alternation(vocab.weekdayLong);\n case 'EEE': return alternation(vocab.weekdayShort);\n case 'a': return alternation(vocab.dayPeriod);\n case 'zzz': return getTimeZoneFragment();\n default:\n throw new Error(`temporal-fmt: unknown token \"${token}\"`);\n }\n}\n\n// Builds one anchored regex from tokenize() output — matches exactly what\n// format() could have produced for this format string, in this locale.\nexport function buildPatternSource(pieces: Piece[], locale: string): string {\n let source = '';\n for (const piece of pieces) {\n source += piece.kind === 'literal' ? escapeRegExp(piece.value) : tokenFragment(piece.value, locale);\n }\n return `^(?:${source})$`;\n}\n","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildPatternSource } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\nconst patternCache = new Map<string, RegExp>();\nconst MAX_CACHE_SIZE = 500;\n\nfunction getPattern(formatStr: string, locale: string): RegExp {\n // \\0 can't appear in a locale tag or format string, so it's a safe join char\n const key = locale + '\\0' + formatStr;\n let pattern = patternCache.get(key);\n if (pattern) {\n return pattern;\n }\n if (patternCache.size >= MAX_CACHE_SIZE) {\n const oldestKey = patternCache.keys().next().value;\n if (oldestKey !== undefined) patternCache.delete(oldestKey);\n }\n const source = buildPatternSource(tokenize(formatStr), locale);\n pattern = new RegExp(source, 'u');\n patternCache.set(key, pattern);\n return pattern;\n}\n\n/**\n * Checks if `input` could plausibly be format()'s output for this format\n * string. Shape and vocabulary only — no parsing, and Feb 30 still passes.\n *\n * @example\n * matchesFormat('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // true\n * matchesFormat('yyyy-MM', '2026-08-04T15:45:30') // false\n */\nexport function matchesFormat(formatStr: string, input: string, options: FormatOptions = {}): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n return getPattern(formatStr, locale).test(input);\n}\n"],"mappings":"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","names":["pad","n","len","DEFAULT_LOCALE","formatterCache","MAX_CACHE_SIZE","getFormatter","locale","options","key","formatter","oldestKey","intlPart","temporal","partType","toInstant","timeZoneId","isZoned","intlSafeTemporal","calendar","formatterOptions","part","p","TOKENS","SORTED_TOKEN_STRINGS","TOKENS","tok","a","b","tokenize","format","pieces","i","ch","appendLiteral","j","literal","closed","match","value","last","HANDLER_BY_TOKEN","TOKENS","tok","fn","field","format","temporal","formatStr","options","locale","DEFAULT_LOCALE","pieces","tokenize","result","piece","handler","vocabCache","partValue","formatter","date","type","part","p","getLocaleVocab","locale","cached","monthLongFmt","monthShortFmt","monthLong","monthShort","m","weekdayLongFmt","weekdayShortFmt","weekdayLong","weekdayShort","d","dayPeriodFmt","am","pm","dayPeriod","vocab","escapeRegExp","literal","alternation","values","timeZoneFragment","getTimeZoneFragment","supportedValuesOf","NUMERIC_FRAGMENTS","tokenFragment","token","locale","numeric","vocab","getLocaleVocab","buildPatternSource","pieces","source","piece","patternCache","MAX_CACHE_SIZE","getPattern","formatStr","locale","key","pattern","oldestKey","source","buildPatternSource","tokenize","matchesFormat","input","options","DEFAULT_LOCALE"]}
|
package/package.json
CHANGED