temporal-fmt 0.9.71 → 0.9.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -49
- package/dist/{chunk-MXXTJ5CW.js → chunk-5KP5H7X2.js} +3 -3
- package/dist/{chunk-ZT3UXRAA.js → chunk-DMJWJTFY.js} +3 -8
- package/dist/chunk-DMJWJTFY.js.map +1 -0
- package/dist/{chunk-CQJYB7E7.js → chunk-HGRT37LT.js} +4 -10
- package/dist/chunk-HGRT37LT.js.map +1 -0
- package/dist/{chunk-3FMUGYE2.js → chunk-KV6GMX4D.js} +4 -159
- package/dist/chunk-KV6GMX4D.js.map +1 -0
- package/dist/chunk-UGYSEYUQ.js +168 -0
- package/dist/chunk-UGYSEYUQ.js.map +1 -0
- package/dist/{chunk-3SC5AVSY.js → chunk-WEVNKOQA.js} +2 -2
- package/dist/chunk-WP7TBCTT.js +241 -0
- package/dist/chunk-WP7TBCTT.js.map +1 -0
- package/dist/{chunk-BQET6RUR.js → chunk-Z245NHDT.js} +2 -19
- package/dist/{chunk-BQET6RUR.js.map → chunk-Z245NHDT.js.map} +1 -1
- package/dist/format.cjs +0 -17
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +3 -3
- package/dist/index.cjs +3832 -4705
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -103
- package/dist/index.d.ts +20 -103
- package/dist/index.js +2406 -846
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +106 -4773
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +5 -10
- package/dist/parse.cjs +0 -13
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +3 -3
- package/dist/recurrence.js +2 -1
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +2 -2
- package/dist/tokens.d.cts +0 -4
- package/dist/tokens.d.ts +0 -4
- package/package.json +5 -21
- package/MODS.md +0 -148
- package/dist/chunk-3FMUGYE2.js.map +0 -1
- package/dist/chunk-CQJYB7E7.js.map +0 -1
- package/dist/chunk-ZT3UXRAA.js.map +0 -1
- package/dist/chunk-ZTIF4ZHX.js +0 -3041
- package/dist/chunk-ZTIF4ZHX.js.map +0 -1
- package/dist/modApi.d.cts +0 -147
- package/dist/modApi.d.ts +0 -147
- package/dist/runtime.d.cts +0 -206
- package/dist/runtime.d.ts +0 -206
- package/scripts/cli.mjs +0 -324
- package/scripts/loadMods.d.mts +0 -37
- package/scripts/loadMods.mjs +0 -801
- package/scripts/managePermissions.mjs +0 -126
- package/scripts/modConfig.mjs +0 -96
- package/scripts/modSandbox.d.mts +0 -13
- package/scripts/modSandbox.mjs +0 -855
- package/scripts/modWire.mjs +0 -241
- package/scripts/modWorker.mjs +0 -574
- package/scripts/semverRange.mjs +0 -157
- package/scripts/versions.json +0 -42
- /package/dist/{chunk-MXXTJ5CW.js.map → chunk-5KP5H7X2.js.map} +0 -0
- /package/dist/{chunk-3SC5AVSY.js.map → chunk-WEVNKOQA.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getTemporal, subscribeToTemporalChanges } from './temporalProvider.js';\nimport { canonicalCacheKey, getCustomVocab, normalizeLocaleTag, partValue } from './localeVocab.js';\nimport { InvalidLocaleError, FormatSyntaxError } from './errors.js';\nimport { isoWeekYearAndWeek, dayOfYear } from './isoWeek.js';\n\n// Throws the library's standard typed missing-field error for tokens\n// that READ more fields than their TOKENS-table `field` entry declares\n// (the table only drives format()'s single-field precheck). ww/RRRR read\n// year/month/day/dayOfWeek but precheck only dayOfWeek; D/DD/DDD read\n// year/month/day but precheck only day. Without this, a bag carrying the\n// declared field but missing the rest fed undefined into the math and\n// produced literal \"NaN\" output instead of the descriptive error every\n// other token throws.\nfunction requireFields(t: TemporalLike, token: string, ...fields: Array<keyof TemporalLike>): void {\n for (const field of fields) {\n if (t[field] === undefined) {\n throw new FormatSyntaxError({\n token,\n message:\n `temporal-fmt: token \"${token}\" requires \"${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}\n\nexport function pad(n: number, len: number): string {\n // padStart pads the whole string, sign included, so pad(-45, 4) used to\n // come out \"0-45\" instead of \"-045\" — split the sign off first.\n const negative = n < 0;\n const digits = String(Math.abs(n)).padStart(len, '0');\n return negative ? '-' + digits : digits;\n}\n\n// Combines the three sub-second fields Temporal exposes into one 9-digit\n// nanosecond-of-second value, then truncates (never rounds) to the\n// requested width. Truncating matches what every digit-width token in\n// this library already does elsewhere (yy, MM, dd, ...) — the token\n// asked for N digits of precision, not a rounded N-digit approximation.\n// A caller asking for SSS on a value with nanosecond precision gets the\n// leading 3 digits of it, same as they'd get the leading 3 digits of any\n// other multi-digit field this library formats.\n//\n// Attached to pad() rather than declared standalone: the bundler's\n// per-function coverage instrumentation attributes hits to each of the\n// 9 fraction-token arrow functions individually but not to a shared\n// helper they all close over, so a correctly-exercised helper still\n// shows as 0 hits under c8. pad() itself is called directly all over\n// this file and is reliably attributed — routing through it here keeps\n// the coverage numbers honest without duplicating the slice logic\n// across every token entry below.\npad.fraction = function formatFraction(t: TemporalLike, width: number): string {\n const nanoOfSecond = t.millisecond! * 1_000_000 + (t.microsecond ?? 0) * 1_000 + (t.nanosecond ?? 0);\n return pad(nanoOfSecond, 9).slice(0, width);\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 microsecond?: number;\n nanosecond?: number;\n timeZoneId?: string;\n // ZonedDateTime.prototype.offset — `±HH:MM` (6 chars) for any modern\n // date, but pre-1900 local-mean-time zones (e.g. America/New_York\n // before 1883) surface a seconds component too: `±HH:MM:SS` (9\n // chars). Format-side offset tokens must check the length rather\n // than assume 6. Parse-side writes a canonicalized `+HH:MM` here\n // before handing it to Temporal.ZonedDateTime.from as the\n // `timeZone` value.\n offset?: string;\n dayOfWeek?: number; // 1=Mon, 7=Sun, per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n toLocaleString?: (locale: string, options: Intl.DateTimeFormatOptions) => string;\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 * When set on parse(), opts into the lenient split heuristic for ambiguous\n * glued numeric runs (e.g. \"121\" against \"Md\"). Default (false) keeps\n * parse()'s strict behavior — throw on ambiguity rather than guess.\n * See README \"Lenient parse mode\" for the heuristic and why it's opt-in.\n */\n lenient?: boolean;\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 = JSON.stringify([canonicalCacheKey(locale), 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 try {\n formatter = new Intl.DateTimeFormat(normalizeLocaleTag(locale), options);\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead (reached via dayPeriodPart — the\n // 'a' token — on any runtime, and the native-Intl path for the rest).\n // Every failure mode of this constructor with a string locale is a\n // RangeError, so converting unconditionally preserves the original\n // message in `reason` either way.\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Passing a Temporal object straight into `new Intl.DateTimeFormat().formatToParts()`\n// only works when the engine's Intl implementation has special-cased support for\n// *native* Temporal instances (checked via internal slots and/or gated behind a V8 flag,\n// not tied to a specific Node version).\n//\n// A Temporal polyfill's instances don't have those slots, so the engine falls back to ToNumber() -> .valueOf(),\n// which the polyfill deliberately throws on (\"Cannot use valueOf\").\n// Probed once and memoized and only from intlPart(), so it never\n// runs unless a format string actually uses a locale-aware token.\nlet nativeSupport: boolean | undefined;\n// Invalidate the memoized probe whenever setTemporal() swaps the active\n// implementation — otherwise a probe result from \"is native Temporal\n// supported\" could keep being used after the active implementation is\n// no longer the one that was probed. See setTemporal() in\n// temporalProvider.ts for the other half of this.\nsubscribeToTemporalChanges(() => { nativeSupport = undefined; });\n\nfunction intlSupportsNativeTemporal(): boolean {\n if (nativeSupport === undefined) {\n nativeSupport = false;\n try {\n const temporal = getTemporal();\n new Intl.DateTimeFormat('en-US', { day: 'numeric' })\n .formatToParts(temporal.PlainDate.from({ year: 1970, month: 1, day: 1 }) as Date);\n // Version-gated, not dead — see the matching note on the\n // !intlSupportsNativeTemporal() branch in intlPart() below for\n // why this can't be exercised from this environment.\n /* c8 ignore next */\n nativeSupport = true;\n /* c8 ignore start */\n } catch {\n // native Temporal absent, or present but not recognized by Intl — fall back.\n // Version-gated, not dead: this catch only fires on runtimes where the\n // probe above throws (no native Temporal, or Intl doesn't recognize it).\n // On a runtime with full native support (e.g. Node builds where Intl\n // accepts native Temporal instances directly) the try succeeds and this\n // branch is unreachable — mirror case of the block below.\n }\n /* c8 ignore stop */\n }\n return nativeSupport;\n}\n\n// The Julian → Gregorian cutover, as ICU applies it. ICU's gregory\n// calendar (unlike Temporal's, which is proleptic Gregorian throughout)\n// treats every date before October 15, 1582 as a Julian-calendar date:\n// the Julian calendar ran ~10 days behind proleptic Gregorian in that\n// era, so a date Temporal correctly calls 1500-07-05 gets silently\n// reinterpreted by Intl as Julian 1500-06-25 — the wrong month near\n// boundaries, and a weekday shifted by 10 mod 7 = 3 slots everywhere.\n// See tc39/ecma402#1003. Whether the cutover is even observable varies\n// by engine and entry point (V8's plain-Date path disables it; the\n// native-Temporal formatToParts path on some Node 26 builds does not),\n// so this can't be probed reliably at runtime — pre-cutover dates have\n// to be routed around Intl's calendar math entirely.\nconst GREGORIAN_CUTOVER_YEAR = 1582;\nconst GREGORIAN_CUTOVER_MONTH = 10;\nconst GREGORIAN_CUTOVER_DAY = 15;\n\n// True when the Temporal object's own (proleptic-Gregorian, hence\n// trustworthy) fields place it before the ICU cutover. Deliberately\n// NaN-tolerant rather than undefined-checking: a Temporal type that\n// lacks one of these fields (PlainMonthDay has no year, PlainYearMonth\n// no day) feeds undefined into Number(), every NaN comparison is false,\n// and the object falls out as \"not before the cutover\" onto its existing\n// formatting path — no undefined-specific branches to keep covered, and\n// no behavior change for field-partial types.\nfunction isBeforeGregorianCutover(t: TemporalLike): boolean {\n if (t.year !== GREGORIAN_CUTOVER_YEAR) return Number(t.year) < GREGORIAN_CUTOVER_YEAR;\n if (t.month !== GREGORIAN_CUTOVER_MONTH) return Number(t.month) < GREGORIAN_CUTOVER_MONTH;\n return Number(t.day) < GREGORIAN_CUTOVER_DAY;\n}\n\n// Renders a locale-aware month/weekday name for a pre-cutover date\n// WITHOUT ever handing the date itself to Intl. Intl is used purely as a\n// name lookup table indexed by month/weekday number — never as the thing\n// that computes which month/weekday a historical date falls on, which is\n// the computation the Julian cutover corrupts. The number comes straight\n// off the Temporal object's own fields (proleptic-Gregorian-correct by\n// construction, since that's what Temporal is), and the name comes from\n// formatting a safe modern reference date carrying that same number.\n//\n// The reference dates are deliberately the same ones getLocaleVocab()\n// (localeVocab.ts) uses to build the parse-side vocabulary — 2020-mm-01\n// for months, the Monday-anchored 2024-01-01..07 week for weekdays — so\n// format() output for a pre-cutover date is byte-identical to what\n// parse() matches against, and round-trips keep working. partValue()\n// gives us the same adjacent-literal merging (ja-JP's \"8月\") the vocab\n// builder uses, for the same reason.\nfunction preCutoverGregorianName(\n temporal: TemporalLike,\n locale: string,\n formatterOptions: Intl.DateTimeFormatOptions,\n partType: 'month' | 'weekday'\n): string {\n // timeZone: 'UTC' matters: the references are built via Date.UTC\n // (midnight UTC), and without pinning the formatter's zone a host\n // timezone behind UTC would shift them to the previous local day.\n // month/dayOfWeek are guaranteed present — format()'s field precheck\n // for MMMM/MMM ('month') and EEEE/EEE ('dayOfWeek') ran before the\n // token handler was invoked at all.\n const reference = partType === 'month'\n ? new Date(Date.UTC(2020, temporal.month! - 1, 1))\n : new Date(Date.UTC(2024, 0, temporal.dayOfWeek!));\n const formatter = getFormatter(locale, { ...formatterOptions, timeZone: 'UTC' });\n return partValue(formatter, reference, partType);\n}\n\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\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 // For iso8601 specifically, force 'gregory' rather than leaving calendar\n // unset: numeric fields (yyyy/dd, see tokens' pad()-based handlers) are\n // always pulled straight off the object's own ISO fields — so if the\n // *locale* carries a `-u-ca-*` extension (e.g. 'en-u-ca-hebrew') and this\n // step left calendar unset, the formatter would resolve its own default\n // calendar from the locale and format MMMM/EEEE in that calendar while\n // yyyy/dd stay ISO, producing a date that looks internally consistent\n // (a real Hebrew month name next to a real-looking day/year) but names a\n // completely different day than the object actually represents. Forcing\n // 'gregory' here keeps every field of an ISO object's output anchored to\n // the same (ISO/Gregorian) calendar — a locale's calendar extension only\n // takes effect when the object itself already carries a non-ISO calendar\n // (via `.withCalendar()`), matching what the README documents.\n //\n // 'gregory' specifically, not 'iso8601' — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes\n // formatToParts() come back empty for some reason, but 'gregory' doesn't\n // have that problem and Temporal's iso8601 calendar is Gregorian-shaped\n // (proleptic Gregorian throughout, no Julian cutover) so the two agree\n // on every numeric field this library ever reads.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n calendar: calendar && calendar !== 'iso8601' ? calendar : 'gregory',\n };\n\n // Field-bag guard: a plain { year, month, day } object only has\n // Object.prototype.toLocaleString, which ignores both arguments and\n // returns \"[object Object]\". On engines without native Temporal\n // support in Intl this used to be caught below, but on engines where\n // Intl *does* recognize native Temporal instances (Node 26+), a bag\n // skips that branch entirely and reaches formatToParts() directly —\n // which doesn't throw \"[object Object]\", it throws a bare\n // \"RangeError: Invalid time value\" once the bag fails ToNumber()\n // coercion. Neither failure mode is useful to a caller, so check for\n // a real toLocaleString up front, before branching on native support,\n // so the descriptive error fires on every engine.\n const ls = temporal.toLocaleString;\n if (typeof ls !== 'function' || ls === Object.prototype.toLocaleString) {\n throw new Error(\n `temporal-fmt: locale-aware part \"${partType}\" needs a value that implements ` +\n `toLocaleString (a real Temporal object). A plain field bag cannot render ` +\n `locale-aware names — pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`\n );\n }\n\n // Pre-1582 cutover guard. ICU's gregory calendar reinterprets dates\n // before October 15, 1582 under Julian-calendar rules (see the long\n // comment on isBeforeGregorianCutover above for the why), so for those\n // dates Intl must never be handed the Temporal object itself — neither\n // through formatToParts() below nor through toLocaleString() in the\n // polyfill branch — since either route lets ICU's calendar math decide\n // which month/weekday the date falls on, and that's exactly what the\n // cutover corrupts. Route month/weekday name lookups through a safe\n // modern reference date instead (preCutoverGregorianName above). Only\n // month/weekday parts are affected: era is AD/CE either way for CE\n // dates, and timeZoneName depends on the instant, not the calendar.\n // Only Gregorian-shaped objects take this path — a Temporal object\n // carrying a non-Gregorian calendar (hebrew, islamic, ...) has its\n // month/weekday fields in that calendar already, and ICU's\n // non-Gregorian calendars don't apply the Julian cutover at all, so\n // feeding one through a gregory-keyed reference lookup would index the\n // wrong month number into the wrong calendar. Custom vocabs never\n // reach here (localeAwareName resolves them before calling intlPart).\n if (\n (partType === 'month' || partType === 'weekday') &&\n formatterOptions.calendar === 'gregory' &&\n isBeforeGregorianCutover(temporal)\n ) {\n return preCutoverGregorianName(temporal, locale, formatterOptions, partType);\n }\n\n // Temporal.prototype.toLocaleString() is part of the Temporal spec itself:\n // polyfills implement the ICU formatting internally without needing the\n // engine to recognize the object, so it works without native Intl support.\n //\n // Everything from here to the end of this function is genuinely\n // reachable — NOT dead code — but only on a Node build where a global\n // `Temporal` exists AND Intl.DateTimeFormat.formatToParts() recognizes\n // native Temporal instances directly (this is real, observed to vary\n // across Node versions: absent on the Node 22/24 builds this suite has\n // been run against, present on at least one Node 26 build). This\n // environment has no native Temporal (`typeof globalThis.Temporal ===\n // 'undefined'`), so intlSupportsNativeTemporal() always returns false\n // here and this branch can't be exercised from this test suite without\n // faking native-instance recognition, which turned out to be\n // impractical (Intl's native-Temporal detection isn't spoofable via a\n // Proxy or valueOf() shim — see the M-02 regression test in\n // temporalProvider.test.js for the same conclusion reached about the\n // sibling probe function). Coverage numbers for this block will differ\n // between Node versions for that reason; that's expected, not a\n // regression.\n /* c8 ignore start */\n if (!intlSupportsNativeTemporal()) {\n // normalizeLocaleTag: the active Temporal implementation's\n // toLocaleString forwards the locale to Intl.DateTimeFormat, which\n // (unlike this library's cache keys) rejects underscore-separated\n // tags like 'en_US' outright.\n // (Field-bag guard now runs unconditionally above, before this\n // native-support branch, so it's not repeated here.)\n try {\n return ls.call(temporal, normalizeLocaleTag(locale), formatterOptions);\n } catch (err) {\n // The active Temporal implementation forwards the tag to Intl, which\n // throws a bare RangeError for a malformed one — rethrow typed.\n if (err instanceof RangeError) {\n throw new InvalidLocaleError({ actual: locale, reason: err.message });\n }\n throw err;\n }\n }\n\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() because destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n const nativeOptions: Intl.DateTimeFormatOptions = {\n ...formatterOptions,\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, nativeOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const index = parts.findIndex((p) => p.type === partType);\n if (index === -1) {\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 // some locales (ja-JP) split a field across two parts — e.g. month \"8\"\n // plus a counter suffix \"月\" as a separate sibling literal part. Merge in\n // an adjacent literal only if it has no whitespace, so a genuine suffix\n // gets folded in but an ordinary separator (the space before \"AM\") stays\n // a separator. Mirrors partValue() in localeVocab.ts, which builds the\n // vocab this token's output needs to match for parse() to round-trip.\n let value = parts[index]!.value;\n const prev = parts[index - 1];\n const next = parts[index + 1];\n if (prev?.type === 'literal' && !/\\s/.test(prev.value)) value = prev.value + value;\n if (next?.type === 'literal' && !/\\s/.test(next.value)) value = value + next.value;\n return value;\n}\n/* c8 ignore stop */\n\n// Temporal.prototype.toLocaleString() can't isolate a single field the way\n// formatToParts() can — asking for `hour` + `dayPeriod` together returns one\n// joined string (e.g. \"3 in the afternoon\"), and `dayPeriod` alone resolves\n// against a different, non-hour-anchored set of periods (\"in the\n// afternoon\"/\"昼\" instead of \"PM\"/\"午後\"). Day period only depends on the\n// hour anyway, so route it through a plain UTC Date and Intl.DateTimeFormat\n// instead — that's worked the same on every engine regardless of whether\n// Temporal itself is native or polyfilled.\nfunction dayPeriodPart(hour: number, locale: string): string {\n // Custom vocab (when registered) takes precedence over Intl — same\n // contract as the other locale-aware tokens. Intl won't know about a\n // caller-supplied AM/PM string for a made-up locale key, so going\n // through Intl would produce something other than what the caller\n // registered.\n const custom = getCustomVocab(locale);\n if (custom) {\n return hour < 12 ? custom.dayPeriod[0]! : custom.dayPeriod[1]!;\n }\n const date = new Date(Date.UTC(1970, 0, 1, hour));\n const formatter = getFormatter(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const part = formatter.formatToParts(date).find((p) => p.type === 'dayPeriod');\n // Defensive guard, confirmed unreachable on this ICU build: forcing\n // hour12: true (as this call always does) produces a dayPeriod part\n // for every locale checked, including 24-hour-clock locales (ja-JP,\n // zh-CN, th-TH, he-IL) and a wide sweep of less-common tags (dz-BT,\n // bo-CN, am-ET, etc.). Same finding as partValue()'s twin guard in\n // localeVocab.ts. Kept in case a future ICU/locale-data update\n // produces a locale that genuinely omits it.\n /* c8 ignore start */\n if (!part) {\n throw new Error(`temporal-fmt: locale \"${locale}\" produced no \"dayPeriod\" part for token \"a\".`);\n }\n /* c8 ignore stop */\n return part.value;\n}\n\n// Resolves a locale-aware month/weekday name from the registered custom\n// vocab when one exists for this locale, falling through to Intl otherwise.\n// Without this, format() would silently keep producing Intl's strings while\n// parse() matched against the registered vocab — the two would round-trip-fail\n// against each other.\nfunction localeAwareName(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes,\n customArray: string[] | undefined,\n customIndex: number | undefined,\n): string {\n if (customArray && customIndex !== undefined && customIndex >= 0 && customIndex < customArray.length) {\n return customArray[customIndex]!;\n }\n return intlPart(temporal, locale, options, partType);\n}\n\n// Formats a `±HH:MM` offset string (the shape Temporal exposes on\n// ZonedDateTime.prototype.offset) into one of the six offset-token widths.\n// Width and Z-handling come from the variant letter+case:\n//\n// X / x — short form: minutes omitted when zero, no colon otherwise\n// XX / xx — hours + minutes, no colon\n// XXX / xxx — hours + minutes, with colon\n//\n// Uppercase (X) collapses +00:00 to \"Z\"; lowercase (x) always emits a\n// numeric offset, even for UTC. Mirrors the date-fns/Unicode-LDML offset\n// family — see README for the full variant table.\nfunction formatOffset(offset: string, variant: 'X' | 'XX' | 'XXX' | 'x' | 'xx' | 'xxx'): string {\n if (offset === '+00:00' && (variant === 'X' || variant === 'XX' || variant === 'XXX')) {\n return 'Z';\n }\n // Most offsets are 6 chars: sign + HH + ':' + MM. Pre-1900 local-mean-time\n // zones can surface a seconds component too, 9 chars: sign + HH + ':' +\n // MM + ':' + SS. \"xxx\" is the one variant with a always-signed,\n // never-\"Z\" colon-separated shape wide enough to carry that unchanged,\n // so it passes a sub-minute offset through verbatim. Every other\n // variant has no seconds slot and must throw rather than silently\n // truncate them away.\n if (offset.length > 6) {\n if (variant === 'xxx') {\n return offset;\n }\n throw new Error(\n `temporal-fmt: token \"${variant}\" cannot represent the offset \"${offset}\", ` +\n `which has a seconds component. None of the X/XX/XXX/x/xx tokens ` +\n `support offset seconds; use \"xxx\" instead, which formats the full ` +\n `offset unchanged.`\n );\n }\n const sign = offset[0]!;\n const hours = offset.slice(1, 3);\n const minutes = offset.slice(4, 6);\n switch (variant) {\n case 'X': case 'x':\n // minutes only matter when they're non-zero — otherwise drop them\n // entirely. Matches LDML: \"With a single X, the hours field is\n // required. The minutes field is optional, but only if the\n // minutes value is 0.\"\n return minutes === '00' ? `${sign}${hours}` : `${sign}${hours}${minutes}`;\n case 'XX': case 'xx':\n return `${sign}${hours}${minutes}`;\n case 'XXX': case 'xxx':\n return `${sign}${hours}:${minutes}`;\n }\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 digits isn't as simple\n// as padding \"3\", and most consumers parsing these back out want plain\n// 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 // Unpadded year — no minimum width, unlike yyyy's fixed 4 digits.\n // pad(n, 0) still does the right thing here: Math.abs(n) with no\n // padStart floor just yields the plain digit string, and the sign\n // handling (split off before padding) already covers negative years,\n // so this doesn't need its own sign branch the way \"yy\" does.\n ['y', (t) => pad(t.year!, 0), 'year'],\n ['MMMM', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n }, 'month'],\n ['MMM', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n }, '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) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n ['EEE', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n }, '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 // Fractional-second tokens, S through SSSSSSSSS (1-9 digits). Each token\n // formats a slice of the same underlying nanosecond-of-second value —\n // combining millisecond/microsecond/nanosecond into one 9-digit number\n // and truncating to the token's width — so \"SSS\" keeps meaning exactly\n // what it always meant (3-digit milliseconds) while wider tokens expose\n // the precision Temporal actually carries. formatFraction below is the\n // shared implementation; see its comment for the truncate-not-round\n // rule and why.\n ['SSSSSSSSS', (t) => pad.fraction(t, 9), 'millisecond'],\n ['SSSSSSSS', (t) => pad.fraction(t, 8), 'millisecond'],\n ['SSSSSSS', (t) => pad.fraction(t, 7), 'millisecond'],\n ['SSSSSS', (t) => pad.fraction(t, 6), 'millisecond'],\n ['SSSSS', (t) => pad.fraction(t, 5), 'millisecond'],\n ['SSSS', (t) => pad.fraction(t, 4), 'millisecond'],\n ['SSS', (t) => pad.fraction(t, 3), 'millisecond'],\n ['SS', (t) => pad.fraction(t, 2), 'millisecond'],\n ['S', (t) => pad.fraction(t, 1), '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) => dayPeriodPart(t.hour!, locale), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n // Numeric UTC offset tokens (date-fns/Unicode-LDML family). Only\n // ZonedDateTime carries an offset, so the field check in format() throws\n // the same \"requires offset, which this Temporal object doesn't have\"\n // error zzz throws on PlainDate/PlainTime/PlainDateTime — same\n // validation path, just a different field name. See formatOffset above\n // for the per-variant width and Z/numeric distinction.\n ['xxx', (t) => formatOffset(t.offset!, 'xxx'), 'offset'],\n ['xx', (t) => formatOffset(t.offset!, 'xx'), 'offset'],\n ['X', (t) => formatOffset(t.offset!, 'X'), 'offset'],\n ['XX', (t) => formatOffset(t.offset!, 'XX'), 'offset'],\n ['XXX', (t) => formatOffset(t.offset!, 'XXX'), 'offset'],\n ['x', (t) => formatOffset(t.offset!, 'x'), 'offset'],\n\n // Ordinal day (1st, 2nd, 3rd, ... 21st). English suffix rules only —\n // locale-aware ordinals (\"2.\" in de-DE, \"2日\" in ja-JP) are out of scope,\n // since the rest of this library routes locale-specific names through\n // Intl.DateTimeFormat, and Intl has no part type for ordinals. Format-only:\n // the suffix isn't structurally distinguishable from a literal in a parse\n // context (a \"st\"/\"nd\"/\"rd\"/\"th\" suffix isn't a digit and would collide\n // with any adjacent literal text), so there's no good way to read it back.\n ['do', (t) => {\n const day = t.day!;\n const lastDigit = day % 10;\n // 11, 12, 13 are the exception — they'd otherwise match the 1/2/3 rule\n // and produce \"11st\"/\"12nd\"/\"13rd\", which is wrong. They always take \"th\".\n const lastTwoDigits = day % 100;\n if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {\n return day + 'th';\n }\n if (lastDigit === 1) return day + 'st';\n if (lastDigit === 2) return day + 'nd';\n if (lastDigit === 3) return day + 'rd';\n return day + 'th';\n }, 'day'],\n\n // Quarter computed from month: 1-3=Q1, 4-6=Q2, 7-9=Q3, 10-12=Q4.\n // `Q` is plain numeric, `QQQ` renders as \"Q3\" — same convention as\n // date-fns's `Q` and `QQQ` for parity with the most common prior art.\n // Both format and parse; parse() cross-checks Q/QQQ against the parsed\n // month in the same spirit as the EEEE-vs-date cross-check.\n ['Q', (t) => String(Math.ceil(t.month! / 3)), 'month'],\n ['QQQ', (t) => 'Q' + Math.ceil(t.month! / 3), 'month'],\n\n // ISO 8601 week and week-numbering year. Both are format-only — parsing\n // \"ww\"/\"RRRR\" back into a real date requires resolving an ISO week + a\n // weekday (or some other disambiguator) to a specific date, which is a\n // different parsing surface than the token-based parse() here. The\n // ISO-week year (RRRR) can differ from the calendar year at the boundary:\n // Dec 29-31 often belong to week 1 of the *next* year; Jan 1-3 often\n // belong to week 52/53 of the *previous* year. See isoWeekYearAndWeek().\n ['ww', (t) => {\n requireFields(t, 'ww', 'year', 'month', 'day');\n const { week } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(week, 2);\n }, 'dayOfWeek'],\n ['RRRR', (t) => {\n requireFields(t, 'RRRR', 'year', 'month', 'day');\n const { isoYear } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(isoYear, 4);\n }, 'dayOfWeek'],\n\n // Day of year — number of days since Jan 1 (1-366). Three widths:\n // D — unpadded (1, 2, 366)\n // DD — 2-digit minimum (zero-padded if <100)\n // DDD — 3-digit zero-padded (001, 002, 366)\n // Format-only — parsing day-of-year requires resolving against a year,\n // which is a different shape from the token-based parse() surface.\n // The dayOfYearHelper() in calendarUtils.ts covers the same field for\n // callers who need the numeric value.\n ['D', (t) => {\n requireFields(t, 'D', 'year', 'month');\n return String(dayOfYear(t.year!, t.month!, t.day!));\n }, 'day'],\n ['DD', (t) => {\n requireFields(t, 'DD', 'year', 'month');\n return pad(dayOfYear(t.year!, t.month!, t.day!), 2);\n }, 'day'],\n ['DDD', (t) => {\n requireFields(t, 'DDD', 'year', 'month');\n return pad(dayOfYear(t.year!, t.month!, t.day!), 3);\n }, 'day'],\n\n // Stand-alone month — uses Intl's stand-alone form. In most locales\n // (en, fr, de, es) this is identical to MMMM/MMM. In Slavic/Baltic\n // locales (cs, sk, pl, ru) the stand-alone form differs from the\n // format form (nominative vs genitive case). LLLL = long, LLL = short.\n ['LLLL', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n }, 'month'],\n ['LLL', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n }, 'month'],\n\n // Stand-alone weekday — same pattern as stand-alone month but for\n // weekday names. cccc = long, ccc = short.\n ['cccc', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n ['ccc', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n\n // Era — locale-aware (\"AD\"/\"BC\" in en, \"ap. J.-C.\"/\"av. J.-C.\" in fr).\n // GGGG = long, G = short. Format-only.\n ['GGGG', (t, locale) => {\n return intlPart(t, locale, { era: 'long' }, 'era');\n }, 'year'],\n ['G', (t, locale) => {\n return intlPart(t, locale, { era: 'short' }, 'era');\n }, 'year'],\n\n // Localized timezone name — uses Intl's longLocalized/short timezone\n // name option. Format-only — these names are locale-dependent and\n // vary by season (EST vs EDT), so parsing them back requires a\n // lookup table that isn't practical to ship.\n ['zzzz', (t, locale) => {\n return intlPart(t, locale, { timeZoneName: 'longGeneric' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n }, 'timeZoneId'],\n ['z', (t, locale) => {\n return intlPart(t, locale, { timeZoneName: 'short' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n }, 'timeZoneId'],\n\n];\n\n// Mod-registered tokens, layered on top of the static TOKENS above.\n// Last-write-wins by name (same rule as registerLocale and\n// createFormatter's own token merging) — a second registerFormatToken()\n// call for a name already claimed, whether that name came from another\n// mod or from TOKENS itself, replaces the earlier entry. See\n// registerFormatToken() in runtime.ts for why this beats a hard error:\n// Mod.priority exists precisely so an author can control who wins a\n// shared key, and that only means something if collisions actually\n// resolve instead of throwing.\nconst registeredTokens = new Map<string, [string, TokenHandler, keyof TemporalLike]>();\n\n// tokenize.ts and format.ts each derive a lookup table from TOKENS at\n// import time (SORTED_TOKEN_STRINGS, HANDLER_BY_TOKEN). Once mods can\n// add tokens after those tables are built, something has to tell both\n// modules to rebuild — this is that hook. Both subscribers rebuild in\n// the same call so the tokenizer's idea of \"is this a token\" and\n// format()'s idea of \"what does this token do\" can never drift apart\n// (the c8-ignored \"impossible\" branches in format.ts assume exactly\n// that they can't).\nconst rebuildListeners: Array<() => void> = [];\n\nexport function onTokenTableChange(listener: () => void): void {\n rebuildListeners.push(listener);\n}\n\n// All tokens currently in effect: built-ins plus mod-registered,\n// mod-registered winning on a name clash. Recomputed on every call\n// rather than cached here — registration only happens at mod-load\n// time, so there's no hot-path cost to paying for the rebuild each time\n// a listener fires.\nexport function getEffectiveTokens(): Array<[string, TokenHandler, keyof TemporalLike]> {\n const merged = new Map(TOKENS.map((t) => [t[0], t] as const));\n for (const [name, entry] of registeredTokens) merged.set(name, entry);\n return [...merged.values()];\n}\n\nexport function registerToken(token: [string, TokenHandler, keyof TemporalLike]): void {\n registeredTokens.set(token[0], token);\n for (const listener of rebuildListeners) listener();\n}\n\n// Test-only: clears mod-registered tokens and notifies subscribers, so\n// one test file's registerFormatToken() call can't leak into the next.\n// Mirrors _resetOverridesForTesting() in runtime.ts.\nexport function _resetRegisteredTokensForTesting(): void {\n registeredTokens.clear();\n for (const listener of rebuildListeners) listener();\n}"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/*\n * Copyright 2026 DirazCoder\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getTemporal, subscribeToTemporalChanges } from './temporalProvider.js';\nimport { canonicalCacheKey, getCustomVocab, normalizeLocaleTag, partValue } from './localeVocab.js';\nimport { InvalidLocaleError, FormatSyntaxError } from './errors.js';\nimport { isoWeekYearAndWeek, dayOfYear } from './isoWeek.js';\n\n// Throws the library's standard typed missing-field error for tokens\n// that READ more fields than their TOKENS-table `field` entry declares\n// (the table only drives format()'s single-field precheck). ww/RRRR read\n// year/month/day/dayOfWeek but precheck only dayOfWeek; D/DD/DDD read\n// year/month/day but precheck only day. Without this, a bag carrying the\n// declared field but missing the rest fed undefined into the math and\n// produced literal \"NaN\" output instead of the descriptive error every\n// other token throws.\nfunction requireFields(t: TemporalLike, token: string, ...fields: Array<keyof TemporalLike>): void {\n for (const field of fields) {\n if (t[field] === undefined) {\n throw new FormatSyntaxError({\n token,\n message:\n `temporal-fmt: token \"${token}\" requires \"${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}\n\nexport function pad(n: number, len: number): string {\n // padStart pads the whole string, sign included, so pad(-45, 4) used to\n // come out \"0-45\" instead of \"-045\" — split the sign off first.\n const negative = n < 0;\n const digits = String(Math.abs(n)).padStart(len, '0');\n return negative ? '-' + digits : digits;\n}\n\n// Combines the three sub-second fields Temporal exposes into one 9-digit\n// nanosecond-of-second value, then truncates (never rounds) to the\n// requested width. Truncating matches what every digit-width token in\n// this library already does elsewhere (yy, MM, dd, ...) — the token\n// asked for N digits of precision, not a rounded N-digit approximation.\n// A caller asking for SSS on a value with nanosecond precision gets the\n// leading 3 digits of it, same as they'd get the leading 3 digits of any\n// other multi-digit field this library formats.\n//\n// Attached to pad() rather than declared standalone: the bundler's\n// per-function coverage instrumentation attributes hits to each of the\n// 9 fraction-token arrow functions individually but not to a shared\n// helper they all close over, so a correctly-exercised helper still\n// shows as 0 hits under c8. pad() itself is called directly all over\n// this file and is reliably attributed — routing through it here keeps\n// the coverage numbers honest without duplicating the slice logic\n// across every token entry below.\npad.fraction = function formatFraction(t: TemporalLike, width: number): string {\n const nanoOfSecond = t.millisecond! * 1_000_000 + (t.microsecond ?? 0) * 1_000 + (t.nanosecond ?? 0);\n return pad(nanoOfSecond, 9).slice(0, width);\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 microsecond?: number;\n nanosecond?: number;\n timeZoneId?: string;\n // ZonedDateTime.prototype.offset — `±HH:MM` (6 chars) for any modern\n // date, but pre-1900 local-mean-time zones (e.g. America/New_York\n // before 1883) surface a seconds component too: `±HH:MM:SS` (9\n // chars). Format-side offset tokens must check the length rather\n // than assume 6. Parse-side writes a canonicalized `+HH:MM` here\n // before handing it to Temporal.ZonedDateTime.from as the\n // `timeZone` value.\n offset?: string;\n dayOfWeek?: number; // 1=Mon, 7=Sun, per Temporal spec\n calendarId?: string;\n toInstant?: () => unknown;\n toLocaleString?: (locale: string, options: Intl.DateTimeFormatOptions) => string;\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 * When set on parse(), opts into the lenient split heuristic for ambiguous\n * glued numeric runs (e.g. \"121\" against \"Md\"). Default (false) keeps\n * parse()'s strict behavior — throw on ambiguity rather than guess.\n * See README \"Lenient parse mode\" for the heuristic and why it's opt-in.\n */\n lenient?: boolean;\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 = JSON.stringify([canonicalCacheKey(locale), 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 try {\n formatter = new Intl.DateTimeFormat(normalizeLocaleTag(locale), options);\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead (reached via dayPeriodPart — the\n // 'a' token — on any runtime, and the native-Intl path for the rest).\n // Every failure mode of this constructor with a string locale is a\n // RangeError, so converting unconditionally preserves the original\n // message in `reason` either way.\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n formatterCache.set(key, formatter);\n return formatter;\n}\n\n// Passing a Temporal object straight into `new Intl.DateTimeFormat().formatToParts()`\n// only works when the engine's Intl implementation has special-cased support for\n// *native* Temporal instances (checked via internal slots and/or gated behind a V8 flag,\n// not tied to a specific Node version).\n//\n// A Temporal polyfill's instances don't have those slots, so the engine falls back to ToNumber() -> .valueOf(),\n// which the polyfill deliberately throws on (\"Cannot use valueOf\").\n// Probed once and memoized and only from intlPart(), so it never\n// runs unless a format string actually uses a locale-aware token.\nlet nativeSupport: boolean | undefined;\n// Invalidate the memoized probe whenever setTemporal() swaps the active\n// implementation — otherwise a probe result from \"is native Temporal\n// supported\" could keep being used after the active implementation is\n// no longer the one that was probed. See setTemporal() in\n// temporalProvider.ts for the other half of this.\nsubscribeToTemporalChanges(() => { nativeSupport = undefined; });\n\nfunction intlSupportsNativeTemporal(): boolean {\n if (nativeSupport === undefined) {\n nativeSupport = false;\n try {\n const temporal = getTemporal();\n new Intl.DateTimeFormat('en-US', { day: 'numeric' })\n .formatToParts(temporal.PlainDate.from({ year: 1970, month: 1, day: 1 }) as Date);\n // Version-gated, not dead — see the matching note on the\n // !intlSupportsNativeTemporal() branch in intlPart() below for\n // why this can't be exercised from this environment.\n /* c8 ignore next */\n nativeSupport = true;\n /* c8 ignore start */\n } catch {\n // native Temporal absent, or present but not recognized by Intl — fall back.\n // Version-gated, not dead: this catch only fires on runtimes where the\n // probe above throws (no native Temporal, or Intl doesn't recognize it).\n // On a runtime with full native support (e.g. Node builds where Intl\n // accepts native Temporal instances directly) the try succeeds and this\n // branch is unreachable — mirror case of the block below.\n }\n /* c8 ignore stop */\n }\n return nativeSupport;\n}\n\n// The Julian → Gregorian cutover, as ICU applies it. ICU's gregory\n// calendar (unlike Temporal's, which is proleptic Gregorian throughout)\n// treats every date before October 15, 1582 as a Julian-calendar date:\n// the Julian calendar ran ~10 days behind proleptic Gregorian in that\n// era, so a date Temporal correctly calls 1500-07-05 gets silently\n// reinterpreted by Intl as Julian 1500-06-25 — the wrong month near\n// boundaries, and a weekday shifted by 10 mod 7 = 3 slots everywhere.\n// See tc39/ecma402#1003. Whether the cutover is even observable varies\n// by engine and entry point (V8's plain-Date path disables it; the\n// native-Temporal formatToParts path on some Node 26 builds does not),\n// so this can't be probed reliably at runtime — pre-cutover dates have\n// to be routed around Intl's calendar math entirely.\nconst GREGORIAN_CUTOVER_YEAR = 1582;\nconst GREGORIAN_CUTOVER_MONTH = 10;\nconst GREGORIAN_CUTOVER_DAY = 15;\n\n// True when the Temporal object's own (proleptic-Gregorian, hence\n// trustworthy) fields place it before the ICU cutover. Deliberately\n// NaN-tolerant rather than undefined-checking: a Temporal type that\n// lacks one of these fields (PlainMonthDay has no year, PlainYearMonth\n// no day) feeds undefined into Number(), every NaN comparison is false,\n// and the object falls out as \"not before the cutover\" onto its existing\n// formatting path — no undefined-specific branches to keep covered, and\n// no behavior change for field-partial types.\nfunction isBeforeGregorianCutover(t: TemporalLike): boolean {\n if (t.year !== GREGORIAN_CUTOVER_YEAR) return Number(t.year) < GREGORIAN_CUTOVER_YEAR;\n if (t.month !== GREGORIAN_CUTOVER_MONTH) return Number(t.month) < GREGORIAN_CUTOVER_MONTH;\n return Number(t.day) < GREGORIAN_CUTOVER_DAY;\n}\n\n// Renders a locale-aware month/weekday name for a pre-cutover date\n// WITHOUT ever handing the date itself to Intl. Intl is used purely as a\n// name lookup table indexed by month/weekday number — never as the thing\n// that computes which month/weekday a historical date falls on, which is\n// the computation the Julian cutover corrupts. The number comes straight\n// off the Temporal object's own fields (proleptic-Gregorian-correct by\n// construction, since that's what Temporal is), and the name comes from\n// formatting a safe modern reference date carrying that same number.\n//\n// The reference dates are deliberately the same ones getLocaleVocab()\n// (localeVocab.ts) uses to build the parse-side vocabulary — 2020-mm-01\n// for months, the Monday-anchored 2024-01-01..07 week for weekdays — so\n// format() output for a pre-cutover date is byte-identical to what\n// parse() matches against, and round-trips keep working. partValue()\n// gives us the same adjacent-literal merging (ja-JP's \"8月\") the vocab\n// builder uses, for the same reason.\nfunction preCutoverGregorianName(\n temporal: TemporalLike,\n locale: string,\n formatterOptions: Intl.DateTimeFormatOptions,\n partType: 'month' | 'weekday'\n): string {\n // timeZone: 'UTC' matters: the references are built via Date.UTC\n // (midnight UTC), and without pinning the formatter's zone a host\n // timezone behind UTC would shift them to the previous local day.\n // month/dayOfWeek are guaranteed present — format()'s field precheck\n // for MMMM/MMM ('month') and EEEE/EEE ('dayOfWeek') ran before the\n // token handler was invoked at all.\n const reference = partType === 'month'\n ? new Date(Date.UTC(2020, temporal.month! - 1, 1))\n : new Date(Date.UTC(2024, 0, temporal.dayOfWeek!));\n const formatter = getFormatter(locale, { ...formatterOptions, timeZone: 'UTC' });\n return partValue(formatter, reference, partType);\n}\n\nfunction intlPart(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes\n): string {\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 // For iso8601 specifically, force 'gregory' rather than leaving calendar\n // unset: numeric fields (yyyy/dd, see tokens' pad()-based handlers) are\n // always pulled straight off the object's own ISO fields — so if the\n // *locale* carries a `-u-ca-*` extension (e.g. 'en-u-ca-hebrew') and this\n // step left calendar unset, the formatter would resolve its own default\n // calendar from the locale and format MMMM/EEEE in that calendar while\n // yyyy/dd stay ISO, producing a date that looks internally consistent\n // (a real Hebrew month name next to a real-looking day/year) but names a\n // completely different day than the object actually represents. Forcing\n // 'gregory' here keeps every field of an ISO object's output anchored to\n // the same (ISO/Gregorian) calendar — a locale's calendar extension only\n // takes effect when the object itself already carries a non-ISO calendar\n // (via `.withCalendar()`), matching what the README documents.\n //\n // 'gregory' specifically, not 'iso8601' — passing `calendar: 'iso8601'`\n // explicitly alongside a single-field options object makes\n // formatToParts() come back empty for some reason, but 'gregory' doesn't\n // have that problem and Temporal's iso8601 calendar is Gregorian-shaped\n // (proleptic Gregorian throughout, no Julian cutover) so the two agree\n // on every numeric field this library ever reads.\n const calendar = temporal?.calendarId;\n const formatterOptions: Intl.DateTimeFormatOptions = {\n ...options,\n calendar: calendar && calendar !== 'iso8601' ? calendar : 'gregory',\n };\n\n // Field-bag guard: a plain { year, month, day } object only has\n // Object.prototype.toLocaleString, which ignores both arguments and\n // returns \"[object Object]\". On engines without native Temporal\n // support in Intl this used to be caught below, but on engines where\n // Intl *does* recognize native Temporal instances (Node 26+), a bag\n // skips that branch entirely and reaches formatToParts() directly —\n // which doesn't throw \"[object Object]\", it throws a bare\n // \"RangeError: Invalid time value\" once the bag fails ToNumber()\n // coercion. Neither failure mode is useful to a caller, so check for\n // a real toLocaleString up front, before branching on native support,\n // so the descriptive error fires on every engine.\n const ls = temporal.toLocaleString;\n if (typeof ls !== 'function' || ls === Object.prototype.toLocaleString) {\n throw new Error(\n `temporal-fmt: locale-aware part \"${partType}\" needs a value that implements ` +\n `toLocaleString (a real Temporal object). A plain field bag cannot render ` +\n `locale-aware names — pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`\n );\n }\n\n // Pre-1582 cutover guard. ICU's gregory calendar reinterprets dates\n // before October 15, 1582 under Julian-calendar rules (see the long\n // comment on isBeforeGregorianCutover above for the why), so for those\n // dates Intl must never be handed the Temporal object itself — neither\n // through formatToParts() below nor through toLocaleString() in the\n // polyfill branch — since either route lets ICU's calendar math decide\n // which month/weekday the date falls on, and that's exactly what the\n // cutover corrupts. Route month/weekday name lookups through a safe\n // modern reference date instead (preCutoverGregorianName above). Only\n // month/weekday parts are affected: era is AD/CE either way for CE\n // dates, and timeZoneName depends on the instant, not the calendar.\n // Only Gregorian-shaped objects take this path — a Temporal object\n // carrying a non-Gregorian calendar (hebrew, islamic, ...) has its\n // month/weekday fields in that calendar already, and ICU's\n // non-Gregorian calendars don't apply the Julian cutover at all, so\n // feeding one through a gregory-keyed reference lookup would index the\n // wrong month number into the wrong calendar. Custom vocabs never\n // reach here (localeAwareName resolves them before calling intlPart).\n if (\n (partType === 'month' || partType === 'weekday') &&\n formatterOptions.calendar === 'gregory' &&\n isBeforeGregorianCutover(temporal)\n ) {\n return preCutoverGregorianName(temporal, locale, formatterOptions, partType);\n }\n\n // Temporal.prototype.toLocaleString() is part of the Temporal spec itself:\n // polyfills implement the ICU formatting internally without needing the\n // engine to recognize the object, so it works without native Intl support.\n //\n // Everything from here to the end of this function is genuinely\n // reachable — NOT dead code — but only on a Node build where a global\n // `Temporal` exists AND Intl.DateTimeFormat.formatToParts() recognizes\n // native Temporal instances directly (this is real, observed to vary\n // across Node versions: absent on the Node 22/24 builds this suite has\n // been run against, present on at least one Node 26 build). This\n // environment has no native Temporal (`typeof globalThis.Temporal ===\n // 'undefined'`), so intlSupportsNativeTemporal() always returns false\n // here and this branch can't be exercised from this test suite without\n // faking native-instance recognition, which turned out to be\n // impractical (Intl's native-Temporal detection isn't spoofable via a\n // Proxy or valueOf() shim — see the M-02 regression test in\n // temporalProvider.test.js for the same conclusion reached about the\n // sibling probe function). Coverage numbers for this block will differ\n // between Node versions for that reason; that's expected, not a\n // regression.\n /* c8 ignore start */\n if (!intlSupportsNativeTemporal()) {\n // normalizeLocaleTag: the active Temporal implementation's\n // toLocaleString forwards the locale to Intl.DateTimeFormat, which\n // (unlike this library's cache keys) rejects underscore-separated\n // tags like 'en_US' outright.\n // (Field-bag guard now runs unconditionally above, before this\n // native-support branch, so it's not repeated here.)\n try {\n return ls.call(temporal, normalizeLocaleTag(locale), formatterOptions);\n } catch (err) {\n // The active Temporal implementation forwards the tag to Intl, which\n // throws a bare RangeError for a malformed one — rethrow typed.\n if (err instanceof RangeError) {\n throw new InvalidLocaleError({ actual: locale, reason: err.message });\n }\n throw err;\n }\n }\n\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() because destructuring it off breaks\n // the receiver and throws\n const intlSafeTemporal = isZoned ? temporal.toInstant!() : temporal;\n const nativeOptions: Intl.DateTimeFormatOptions = {\n ...formatterOptions,\n ...(isZoned ? { timeZone: timeZoneId } : {}),\n };\n\n const formatter = getFormatter(locale, nativeOptions);\n const parts = formatter.formatToParts(intlSafeTemporal as Date | number);\n const index = parts.findIndex((p) => p.type === partType);\n if (index === -1) {\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 // some locales (ja-JP) split a field across two parts — e.g. month \"8\"\n // plus a counter suffix \"月\" as a separate sibling literal part. Merge in\n // an adjacent literal only if it has no whitespace, so a genuine suffix\n // gets folded in but an ordinary separator (the space before \"AM\") stays\n // a separator. Mirrors partValue() in localeVocab.ts, which builds the\n // vocab this token's output needs to match for parse() to round-trip.\n let value = parts[index]!.value;\n const prev = parts[index - 1];\n const next = parts[index + 1];\n if (prev?.type === 'literal' && !/\\s/.test(prev.value)) value = prev.value + value;\n if (next?.type === 'literal' && !/\\s/.test(next.value)) value = value + next.value;\n return value;\n}\n/* c8 ignore stop */\n\n// Temporal.prototype.toLocaleString() can't isolate a single field the way\n// formatToParts() can — asking for `hour` + `dayPeriod` together returns one\n// joined string (e.g. \"3 in the afternoon\"), and `dayPeriod` alone resolves\n// against a different, non-hour-anchored set of periods (\"in the\n// afternoon\"/\"昼\" instead of \"PM\"/\"午後\"). Day period only depends on the\n// hour anyway, so route it through a plain UTC Date and Intl.DateTimeFormat\n// instead — that's worked the same on every engine regardless of whether\n// Temporal itself is native or polyfilled.\nfunction dayPeriodPart(hour: number, locale: string): string {\n // Custom vocab (when registered) takes precedence over Intl — same\n // contract as the other locale-aware tokens. Intl won't know about a\n // caller-supplied AM/PM string for a made-up locale key, so going\n // through Intl would produce something other than what the caller\n // registered.\n const custom = getCustomVocab(locale);\n if (custom) {\n return hour < 12 ? custom.dayPeriod[0]! : custom.dayPeriod[1]!;\n }\n const date = new Date(Date.UTC(1970, 0, 1, hour));\n const formatter = getFormatter(locale, { hour: 'numeric', hour12: true, timeZone: 'UTC' });\n const part = formatter.formatToParts(date).find((p) => p.type === 'dayPeriod');\n // Defensive guard, confirmed unreachable on this ICU build: forcing\n // hour12: true (as this call always does) produces a dayPeriod part\n // for every locale checked, including 24-hour-clock locales (ja-JP,\n // zh-CN, th-TH, he-IL) and a wide sweep of less-common tags (dz-BT,\n // bo-CN, am-ET, etc.). Same finding as partValue()'s twin guard in\n // localeVocab.ts. Kept in case a future ICU/locale-data update\n // produces a locale that genuinely omits it.\n /* c8 ignore start */\n if (!part) {\n throw new Error(`temporal-fmt: locale \"${locale}\" produced no \"dayPeriod\" part for token \"a\".`);\n }\n /* c8 ignore stop */\n return part.value;\n}\n\n// Resolves a locale-aware month/weekday name from the registered custom\n// vocab when one exists for this locale, falling through to Intl otherwise.\n// Without this, format() would silently keep producing Intl's strings while\n// parse() matched against the registered vocab — the two would round-trip-fail\n// against each other.\nfunction localeAwareName(\n temporal: TemporalLike,\n locale: string,\n options: Intl.DateTimeFormatOptions,\n partType: Intl.DateTimeFormatPartTypes,\n customArray: string[] | undefined,\n customIndex: number | undefined,\n): string {\n if (customArray && customIndex !== undefined && customIndex >= 0 && customIndex < customArray.length) {\n return customArray[customIndex]!;\n }\n return intlPart(temporal, locale, options, partType);\n}\n\n// Formats a `±HH:MM` offset string (the shape Temporal exposes on\n// ZonedDateTime.prototype.offset) into one of the six offset-token widths.\n// Width and Z-handling come from the variant letter+case:\n//\n// X / x — short form: minutes omitted when zero, no colon otherwise\n// XX / xx — hours + minutes, no colon\n// XXX / xxx — hours + minutes, with colon\n//\n// Uppercase (X) collapses +00:00 to \"Z\"; lowercase (x) always emits a\n// numeric offset, even for UTC. Mirrors the date-fns/Unicode-LDML offset\n// family — see README for the full variant table.\nfunction formatOffset(offset: string, variant: 'X' | 'XX' | 'XXX' | 'x' | 'xx' | 'xxx'): string {\n if (offset === '+00:00' && (variant === 'X' || variant === 'XX' || variant === 'XXX')) {\n return 'Z';\n }\n // Most offsets are 6 chars: sign + HH + ':' + MM. Pre-1900 local-mean-time\n // zones can surface a seconds component too, 9 chars: sign + HH + ':' +\n // MM + ':' + SS. \"xxx\" is the one variant with a always-signed,\n // never-\"Z\" colon-separated shape wide enough to carry that unchanged,\n // so it passes a sub-minute offset through verbatim. Every other\n // variant has no seconds slot and must throw rather than silently\n // truncate them away.\n if (offset.length > 6) {\n if (variant === 'xxx') {\n return offset;\n }\n throw new Error(\n `temporal-fmt: token \"${variant}\" cannot represent the offset \"${offset}\", ` +\n `which has a seconds component. None of the X/XX/XXX/x/xx tokens ` +\n `support offset seconds; use \"xxx\" instead, which formats the full ` +\n `offset unchanged.`\n );\n }\n const sign = offset[0]!;\n const hours = offset.slice(1, 3);\n const minutes = offset.slice(4, 6);\n switch (variant) {\n case 'X': case 'x':\n // minutes only matter when they're non-zero — otherwise drop them\n // entirely. Matches LDML: \"With a single X, the hours field is\n // required. The minutes field is optional, but only if the\n // minutes value is 0.\"\n return minutes === '00' ? `${sign}${hours}` : `${sign}${hours}${minutes}`;\n case 'XX': case 'xx':\n return `${sign}${hours}${minutes}`;\n case 'XXX': case 'xxx':\n return `${sign}${hours}:${minutes}`;\n }\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 digits isn't as simple\n// as padding \"3\", and most consumers parsing these back out want plain\n// 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 // Unpadded year — no minimum width, unlike yyyy's fixed 4 digits.\n // pad(n, 0) still does the right thing here: Math.abs(n) with no\n // padStart floor just yields the plain digit string, and the sign\n // handling (split off before padding) already covers negative years,\n // so this doesn't need its own sign branch the way \"yy\" does.\n ['y', (t) => pad(t.year!, 0), 'year'],\n ['MMMM', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n }, 'month'],\n ['MMM', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n }, '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) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n ['EEE', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n }, '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 // Fractional-second tokens, S through SSSSSSSSS (1-9 digits). Each token\n // formats a slice of the same underlying nanosecond-of-second value —\n // combining millisecond/microsecond/nanosecond into one 9-digit number\n // and truncating to the token's width — so \"SSS\" keeps meaning exactly\n // what it always meant (3-digit milliseconds) while wider tokens expose\n // the precision Temporal actually carries. formatFraction below is the\n // shared implementation; see its comment for the truncate-not-round\n // rule and why.\n ['SSSSSSSSS', (t) => pad.fraction(t, 9), 'millisecond'],\n ['SSSSSSSS', (t) => pad.fraction(t, 8), 'millisecond'],\n ['SSSSSSS', (t) => pad.fraction(t, 7), 'millisecond'],\n ['SSSSSS', (t) => pad.fraction(t, 6), 'millisecond'],\n ['SSSSS', (t) => pad.fraction(t, 5), 'millisecond'],\n ['SSSS', (t) => pad.fraction(t, 4), 'millisecond'],\n ['SSS', (t) => pad.fraction(t, 3), 'millisecond'],\n ['SS', (t) => pad.fraction(t, 2), 'millisecond'],\n ['S', (t) => pad.fraction(t, 1), '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) => dayPeriodPart(t.hour!, locale), 'hour'],\n ['zzz', (t) => t.timeZoneId!, 'timeZoneId'],\n // Numeric UTC offset tokens (date-fns/Unicode-LDML family). Only\n // ZonedDateTime carries an offset, so the field check in format() throws\n // the same \"requires offset, which this Temporal object doesn't have\"\n // error zzz throws on PlainDate/PlainTime/PlainDateTime — same\n // validation path, just a different field name. See formatOffset above\n // for the per-variant width and Z/numeric distinction.\n ['xxx', (t) => formatOffset(t.offset!, 'xxx'), 'offset'],\n ['xx', (t) => formatOffset(t.offset!, 'xx'), 'offset'],\n ['X', (t) => formatOffset(t.offset!, 'X'), 'offset'],\n ['XX', (t) => formatOffset(t.offset!, 'XX'), 'offset'],\n ['XXX', (t) => formatOffset(t.offset!, 'XXX'), 'offset'],\n ['x', (t) => formatOffset(t.offset!, 'x'), 'offset'],\n\n // Ordinal day (1st, 2nd, 3rd, ... 21st). English suffix rules only —\n // locale-aware ordinals (\"2.\" in de-DE, \"2日\" in ja-JP) are out of scope,\n // since the rest of this library routes locale-specific names through\n // Intl.DateTimeFormat, and Intl has no part type for ordinals. Format-only:\n // the suffix isn't structurally distinguishable from a literal in a parse\n // context (a \"st\"/\"nd\"/\"rd\"/\"th\" suffix isn't a digit and would collide\n // with any adjacent literal text), so there's no good way to read it back.\n ['do', (t) => {\n const day = t.day!;\n const lastDigit = day % 10;\n // 11, 12, 13 are the exception — they'd otherwise match the 1/2/3 rule\n // and produce \"11st\"/\"12nd\"/\"13rd\", which is wrong. They always take \"th\".\n const lastTwoDigits = day % 100;\n if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {\n return day + 'th';\n }\n if (lastDigit === 1) return day + 'st';\n if (lastDigit === 2) return day + 'nd';\n if (lastDigit === 3) return day + 'rd';\n return day + 'th';\n }, 'day'],\n\n // Quarter computed from month: 1-3=Q1, 4-6=Q2, 7-9=Q3, 10-12=Q4.\n // `Q` is plain numeric, `QQQ` renders as \"Q3\" — same convention as\n // date-fns's `Q` and `QQQ` for parity with the most common prior art.\n // Both format and parse; parse() cross-checks Q/QQQ against the parsed\n // month in the same spirit as the EEEE-vs-date cross-check.\n ['Q', (t) => String(Math.ceil(t.month! / 3)), 'month'],\n ['QQQ', (t) => 'Q' + Math.ceil(t.month! / 3), 'month'],\n\n // ISO 8601 week and week-numbering year. Both are format-only — parsing\n // \"ww\"/\"RRRR\" back into a real date requires resolving an ISO week + a\n // weekday (or some other disambiguator) to a specific date, which is a\n // different parsing surface than the token-based parse() here. The\n // ISO-week year (RRRR) can differ from the calendar year at the boundary:\n // Dec 29-31 often belong to week 1 of the *next* year; Jan 1-3 often\n // belong to week 52/53 of the *previous* year. See isoWeekYearAndWeek().\n ['ww', (t) => {\n requireFields(t, 'ww', 'year', 'month', 'day');\n const { week } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(week, 2);\n }, 'dayOfWeek'],\n ['RRRR', (t) => {\n requireFields(t, 'RRRR', 'year', 'month', 'day');\n const { isoYear } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(isoYear, 4);\n }, 'dayOfWeek'],\n\n // Day of year — number of days since Jan 1 (1-366). Three widths:\n // D — unpadded (1, 2, 366)\n // DD — 2-digit minimum (zero-padded if <100)\n // DDD — 3-digit zero-padded (001, 002, 366)\n // Format-only — parsing day-of-year requires resolving against a year,\n // which is a different shape from the token-based parse() surface.\n // The dayOfYearHelper() in calendarUtils.ts covers the same field for\n // callers who need the numeric value.\n ['D', (t) => {\n requireFields(t, 'D', 'year', 'month');\n return String(dayOfYear(t.year!, t.month!, t.day!));\n }, 'day'],\n ['DD', (t) => {\n requireFields(t, 'DD', 'year', 'month');\n return pad(dayOfYear(t.year!, t.month!, t.day!), 2);\n }, 'day'],\n ['DDD', (t) => {\n requireFields(t, 'DDD', 'year', 'month');\n return pad(dayOfYear(t.year!, t.month!, t.day!), 3);\n }, 'day'],\n\n // Stand-alone month — uses Intl's stand-alone form. In most locales\n // (en, fr, de, es) this is identical to MMMM/MMM. In Slavic/Baltic\n // locales (cs, sk, pl, ru) the stand-alone form differs from the\n // format form (nominative vs genitive case). LLLL = long, LLL = short.\n ['LLLL', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'long' }, 'month', custom?.monthLong, t.month! - 1);\n }, 'month'],\n ['LLL', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { month: 'short' }, 'month', custom?.monthShort, t.month! - 1);\n }, 'month'],\n\n // Stand-alone weekday — same pattern as stand-alone month but for\n // weekday names. cccc = long, ccc = short.\n ['cccc', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'long' }, 'weekday', custom?.weekdayLong, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n ['ccc', (t, locale) => {\n const custom = getCustomVocab(locale);\n return localeAwareName(t, locale, { weekday: 'short' }, 'weekday', custom?.weekdayShort, t.dayOfWeek! - 1);\n }, 'dayOfWeek'],\n\n // Era — locale-aware (\"AD\"/\"BC\" in en, \"ap. J.-C.\"/\"av. J.-C.\" in fr).\n // GGGG = long, G = short. Format-only.\n ['GGGG', (t, locale) => {\n return intlPart(t, locale, { era: 'long' }, 'era');\n }, 'year'],\n ['G', (t, locale) => {\n return intlPart(t, locale, { era: 'short' }, 'era');\n }, 'year'],\n\n // Localized timezone name — uses Intl's longLocalized/short timezone\n // name option. Format-only — these names are locale-dependent and\n // vary by season (EST vs EDT), so parsing them back requires a\n // lookup table that isn't practical to ship.\n ['zzzz', (t, locale) => {\n return intlPart(t, locale, { timeZoneName: 'longGeneric' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n }, 'timeZoneId'],\n ['z', (t, locale) => {\n return intlPart(t, locale, { timeZoneName: 'short' as Intl.DateTimeFormatOptions['timeZoneName'] }, 'timeZoneName' as Intl.DateTimeFormatPartTypes);\n }, 'timeZoneId'],\n\n];\n"],"mappings":"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","names":[]}
|
package/dist/format.cjs
CHANGED
|
@@ -603,22 +603,9 @@ var TOKENS = [
|
|
|
603
603
|
return intlPart(t, locale, { timeZoneName: "short" }, "timeZoneName");
|
|
604
604
|
}, "timeZoneId"]
|
|
605
605
|
];
|
|
606
|
-
var registeredTokens = /* @__PURE__ */ new Map();
|
|
607
|
-
var rebuildListeners = [];
|
|
608
|
-
function onTokenTableChange(listener) {
|
|
609
|
-
rebuildListeners.push(listener);
|
|
610
|
-
}
|
|
611
|
-
function getEffectiveTokens() {
|
|
612
|
-
const merged = new Map(TOKENS.map((t) => [t[0], t]));
|
|
613
|
-
for (const [name, entry] of registeredTokens) merged.set(name, entry);
|
|
614
|
-
return [...merged.values()];
|
|
615
|
-
}
|
|
616
606
|
|
|
617
607
|
// src/tokenize.ts
|
|
618
608
|
var SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);
|
|
619
|
-
onTokenTableChange(() => {
|
|
620
|
-
SORTED_TOKEN_STRINGS = getEffectiveTokens().map(([tok]) => tok).sort((a, b) => b.length - a.length);
|
|
621
|
-
});
|
|
622
609
|
function tokenizeWithSpans(format2) {
|
|
623
610
|
const pieces = [];
|
|
624
611
|
let i = 0;
|
|
@@ -785,10 +772,6 @@ function applyNumbering(s, options) {
|
|
|
785
772
|
var HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));
|
|
786
773
|
var tokenizeCache = /* @__PURE__ */ new Map();
|
|
787
774
|
var MAX_TOKENIZE_CACHE_SIZE = 500;
|
|
788
|
-
onTokenTableChange(() => {
|
|
789
|
-
HANDLER_BY_TOKEN = new Map(getEffectiveTokens().map(([tok, fn, field]) => [tok, { fn, field }]));
|
|
790
|
-
tokenizeCache.clear();
|
|
791
|
-
});
|
|
792
775
|
function getPieces(formatStr) {
|
|
793
776
|
let pieces = tokenizeCache.get(formatStr);
|
|
794
777
|
if (pieces) return pieces;
|