temporal-fmt 0.9.2 → 0.9.3
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 +166 -1
- package/dist/chunk-4JDAJSEM.js +35 -0
- package/dist/chunk-4JDAJSEM.js.map +1 -0
- package/dist/{chunk-F4RGUDA3.js → chunk-4XRE37WT.js} +2 -2
- package/dist/chunk-GMGZZG6I.js +2 -0
- package/dist/chunk-GMGZZG6I.js.map +1 -0
- package/dist/{chunk-R52YOKI3.js → chunk-NPKJ7QFK.js} +2 -2
- package/dist/{chunk-NBXF7V5B.js → chunk-R4YEFOVE.js} +2 -2
- package/dist/{chunk-4JWGUR4O.js → chunk-SSPABEUR.js} +2 -2
- package/dist/format.cjs +5 -5
- package/dist/format.cjs.map +1 -1
- package/dist/format.js +1 -1
- package/dist/index.cjs +24 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interval.cjs +2 -2
- package/dist/interval.cjs.map +1 -1
- package/dist/interval.js +1 -1
- package/dist/parse.cjs +9 -9
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +1 -1
- package/dist/pattern.d.cts +1 -0
- package/dist/pattern.d.ts +1 -0
- package/dist/relativeTime.cjs +1 -1
- package/dist/relativeTime.cjs.map +1 -1
- package/dist/relativeTime.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-C5YESWFT.js +0 -2
- package/dist/chunk-C5YESWFT.js.map +0 -1
- package/dist/chunk-YB6YAG7E.js +0 -35
- package/dist/chunk-YB6YAG7E.js.map +0 -1
- /package/dist/{chunk-F4RGUDA3.js.map → chunk-4XRE37WT.js.map} +0 -0
- /package/dist/{chunk-R52YOKI3.js.map → chunk-NPKJ7QFK.js.map} +0 -0
- /package/dist/{chunk-NBXF7V5B.js.map → chunk-R4YEFOVE.js.map} +0 -0
- /package/dist/{chunk-4JWGUR4O.js.map → chunk-SSPABEUR.js.map} +0 -0
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/temporalProvider.ts","../src/errors.ts","../src/localeVocab.ts","../src/isoWeek.ts","../src/tokens.ts","../src/tokenize.ts","../src/numbering.ts","../src/format.ts","../src/pattern.ts","../src/parsePattern.ts","../src/parse.ts","../src/formatDuration.ts","../src/formatDistance.ts","../src/relativeGrammar.ts","../src/parseRelative.ts","../src/typeGuards.ts","../src/tokenMetadata.ts","../src/analyze.ts","../src/calendarUtils.ts","../src/arithmetic.ts","../src/comparison.ts","../src/rounding.ts","../src/serialization.ts","../src/duration.ts","../src/relativeTime.ts","../src/localeRegistry.ts","../src/config.ts","../src/interval.ts","../src/recurrence.ts","../src/businessCalendar.ts","../src/holidays.ts","../src/timezone.ts","../src/extensibility.ts","../src/ideData.ts","../src/codemod.ts"],"sourcesContent":["export { format, formatToParts, compileFormat, type FormattedPart, type CompiledFormat } from './format.js';\nexport { parse, safeParse, tryParse, parseToParts, compileParser, type SafeParseResult, type ParsedPart, type CompiledParser } from './parse.js';\nexport { setTemporal } from './temporalProvider.js';\nexport { formatDuration } from './formatDuration.js';\nexport { formatDistance, formatDistanceToNow } from './formatDistance.js';\nexport { parseRelative } from './parseRelative.js';\nexport { registerLocaleVocab } from './localeVocab.js';\nexport type { LocaleVocab } from './localeVocab.js';\nexport type { TemporalLike, FormatOptions } from './tokens.js';\nexport type { DurationFormatOptions } from './formatDuration.js';\nexport type { FormatDistanceOptions, DistanceCutoffs } from './formatDistance.js';\nexport type { ParseRelativeOptions } from './parseRelative.js';\nexport type { TemporalNamespace } from './temporalProvider.js';\n\n// type guards\nexport {\n isTemporal, isInstant, isPlainDate, isPlainTime, isPlainDateTime,\n isZonedDateTime, isPlainYearMonth, isPlainMonthDay, isDuration,\n assertTemporal, assertInstant, assertPlainDate, assertPlainTime,\n assertPlainDateTime, assertZonedDateTime, assertPlainYearMonth,\n assertPlainMonthDay, assertDuration,\n} from './typeGuards.js';\n\n// typed errors\nexport {\n TemporalFmtError, FormatSyntaxError, UnknownTokenError, ParseMismatchError,\n InvalidDateError, InvalidTimeError, InvalidOffsetError, InvalidTimeZoneError,\n InvalidCalendarError, AmbiguousInputError, InvalidLocaleError, InvalidDurationError,\n type TemporalFmtErrorFields, type TemporalFmtErrorCode,\n} from './errors.js';\n\n// analyzer / introspection\nexport {\n analyzeFormat, explainFormat, tokenizeFormat, listTokens, tokenInfo,\n isValidFormat, validateFormat, fieldForToken,\n type FormatAnalysis, type AnalyzedToken, type TokenMetadata, type TemporalType,\n} from './analyze.js';\n\n// token metadata (re-exported as a top-level surface for\n// callers like the ESLint plugin that want the table directly without\n// going through analyzeFormat)\nexport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\nexport { FORMAT_ONLY_TOKENS } from './pattern.js';\n\n// calendar utilities\nexport {\n daysInMonth, daysInYear, monthsInYear, isLeapYear, isLeapMonth,\n dayOfYearHelper as dayOfYear, weekOfYear, weekYear,\n getQuarter, getMonth, getWeekday, startOf, endOf, asDateFieldView,\n type DateFieldView, type StartOfUnit, type QuarterOptions,\n} from './calendarUtils.js';\n\n// date arithmetic\nexport {\n add, subtract, difference,\n addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addMilliseconds,\n subtractYears, subtractMonths, subtractWeeks, subtractDays, subtractHours, subtractMinutes, subtractSeconds, subtractMilliseconds,\n differenceInYears, differenceInMonths, differenceInWeeks, differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds, differenceInMilliseconds,\n type AddUnit, type DiffUnit,\n} from './arithmetic.js';\n\n// comparison\nexport {\n compare, isEqual, isBefore, isAfter, min, max, clamp, isBetween,\n isToday, isTomorrow, isYesterday,\n isSameDay, isSameWeek, isSameMonth, isSameQuarter, isSameYear,\n isWeekend, isWeekday,\n} from './comparison.js';\n\n// rounding\nexport { round, floor, ceil, truncate, roundDuration, type RoundOptions, type DurationFields } from './rounding.js';\n\n// serialization\nexport {\n parseISO, formatISO,\n parseRFC3339, formatRFC3339,\n parseRFC2822, formatRFC2822,\n parseHTTPDate, formatHTTPDate,\n fromUnixSeconds, fromUnixMilliseconds, fromUnixMicroseconds, fromUnixNanoseconds,\n toUnixSeconds, toUnixMilliseconds, toUnixMicroseconds, toUnixNanoseconds,\n parseSQL, formatSQL,\n} from './serialization.js';\n\n// duration (extended)\nexport {\n formatDurationToParts, parseDuration, parseISODuration, formatISODuration,\n balanceDuration, totalDuration, compareDuration, addDuration, subtractDuration,\n} from './duration.js';\nexport { roundDuration as roundDurationAlias } from './rounding.js';\n\n// relative time\nexport { formatRelative, formatRelativeToNow, type FormatRelativeOptions } from './relativeTime.js';\n\n// locale registration\nexport { registerLocale, getLocale, hasLocale, type ExtendedLocaleVocab } from './localeRegistry.js';\n\n// numbering systems\nexport {\n convertDigits, convertDigitsToAscii, applyNumbering, applyParseNumbering,\n SUPPORTED_NUMBERING_SYSTEMS, type NumberingSystem, type NumberingFormatOptions,\n} from './numbering.js';\n\n// config\nexport { createConfig, mergeWithConfig, DEFAULT_CONFIG, type TemporalFmtConfig } from './config.js';\n\n// relative-grammar registration\nexport { registerRelativeGrammar, listRegisteredGrammars, type RelativeGrammar, type RelativeGrammarMatch } from './relativeGrammar.js';\n\n// intervals\nexport {\n interval, contains as intervalContains, overlaps, intersects,\n isBefore as intervalIsBefore, isAfter as intervalIsAfter,\n intersection, union, difference as intervalDifference,\n subtract as intervalSubtract, mergeIntervals, splitInterval,\n formatRange, formatRangeToParts,\n type Interval, type IntervalBounds,\n} from './interval.js';\n\n// recurrence\nexport {\n recurrence, take, skip, between,\n parseRRule, formatRRule,\n type RecurrenceRule, type RecurrenceFrequency, type RecurrenceIterator,\n} from './recurrence.js';\n\n// business calendar\nexport {\n createBusinessCalendar, isBusinessDay, addBusinessDays, subtractBusinessDays,\n differenceInBusinessDays, nextBusinessDay, previousBusinessDay,\n type BusinessCalendar, type BusinessCalendarOptions,\n} from './businessCalendar.js';\n\n// holiday framework\nexport {\n createHolidayCalendar, nextHoliday, previousHoliday, holidaysBetween,\n type HolidayCalendar, type HolidaySpec,\n} from './holidays.js';\n\n// timezone subsystem\nexport {\n resolveZoned, getTimeZone, getOffset, getOffsetNanoseconds, isDST,\n getNextTransition, getPreviousTransition, getTransitions, possibleInstantsFor,\n type DisambiguationMode, type ResolveZonedOptions,\n} from './timezone.js';\n\n// extensibility\nexport {\n createFormatter,\n type Formatter, type FormatterOptions, type CustomToken, type TokenHandler, type TokenField,\n} from './extensibility.js';\n\n// IDE tooling data\nexport {\n getAutocompleteData, getHoverDocs, getInlineDiagnostics,\n previewFormat, getDocUrl,\n DAYJS_TO_TEMPORAL_FMT, DATE_FNS_TO_TEMPORAL_FMT,\n type TokenAutocompleteEntry, type TokenHoverDoc,\n type InlineDiagnostic, type TokenConversionHint,\n} from './ideData.js';\n\n// Day.js / date-fns format-string translation — backs the CLI's\n// `translate` subcommand (see scripts/cli.mjs).\nexport {\n translateDayjsFormatString, translateDateFnsFormatString,\n} from './codemod.js';\n","// This package's tsconfig assumes lib: [\"ESNext\"] only — no ambient\n// `Temporal` namespace type. Everywhere else in this codebase only ever\n// *reads* fields off a Temporal-like object the caller already built\n// (TemporalLike in tokens.ts). This module is the single choke point for\n// touching a *namespace*-shaped Temporal (PlainDate.from, etc.): parse()\n// uses it to construct a result, tokens.ts uses it to probe native\n// Intl<->Temporal support. Consumers can hand us an implementation via\n// setTemporal(), or we fall back to globalThis.Temporal\ninterface TemporalFactory {\n from(fields: Record<string, number | string | undefined>, options?: { overflow?: 'constrain' | 'reject'; disambiguation?: 'compatible' | 'earlier' | 'later' | 'reject'; offset?: 'use' | 'ignore' | 'prefer' | 'reject' }): unknown;\n // compare() lives on the namespace (Temporal.PlainDate.compare), not on\n // instances. Optional here since a consumer might be passing a\n // stripped-down Temporal shim that doesn't expose it.\n compare?(one: unknown, two: unknown): number;\n}\n\n// Instant is constructed via fromEpochMilliseconds / fromEpochNanoseconds\n// rather than from(fields). Separate interface.\ninterface InstantFactory {\n from(iso: string): unknown;\n fromEpochMilliseconds(ms: number): unknown;\n fromEpochMicroseconds(µs: number): unknown;\n fromEpochNanoseconds(ns: bigint): unknown;\n}\n\n// Duration is constructed via from(fields). Carries round() and compare().\ninterface DurationFactory extends TemporalFactory {\n compare?(one: unknown, two: unknown): number;\n prototype?: { round(options: unknown): unknown };\n}\n\n// PlainYearMonth / PlainMonthDay are also constructed via from(fields).\n// Same factory shape, kept separate for type clarity in callers that\n// want to narrow.\nexport interface TemporalNamespace {\n PlainDate: TemporalFactory;\n PlainTime: TemporalFactory;\n PlainDateTime: TemporalFactory;\n ZonedDateTime: TemporalFactory;\n Instant?: InstantFactory;\n Duration?: DurationFactory;\n PlainYearMonth?: TemporalFactory;\n PlainMonthDay?: TemporalFactory;\n}\n\nlet injectedTemporal: TemporalNamespace | undefined;\n\n// Anything that caches a result derived from *which* Temporal\n// implementation is active (right now: tokens.ts's native-Intl-support\n// probe) registers here so it gets invalidated whenever setTemporal()\n// swaps the implementation — see the comment on setTemporal() below for\n// why that matters. A plain array instead of an event-emitter-style API\n// since this only ever needs \"call every listener, in registration order,\n// with no payload\" — nothing here needs unsubscribe or payload data.\nconst onTemporalChanged: Array<() => void> = [];\n\nexport function subscribeToTemporalChanges(listener: () => void): void {\n onTemporalChanged.push(listener);\n}\n\n/**\n * Explicitly hand temporal-fmt the Temporal implementation to use, instead\n * of relying on a global `Temporal`. Call this once, before your first\n * `format()`/`parse()`/`parseISO()`/etc.\n *\n * Call with no argument (or `undefined`) to clear the override and fall\n * back to `globalThis.Temporal` again.\n *\n * @example\n * import { Temporal } from 'temporal-polyfill';\n * import { setTemporal } from 'temporal-fmt';\n * setTemporal(Temporal);\n */\nexport function setTemporal(temporal?: TemporalNamespace): void {\n injectedTemporal = temporal;\n // tokens.ts's native-Intl-support probe is keyed on whichever Temporal\n // implementation was active the first time a locale-aware format() ran —\n // if that implementation changes later (native -> polyfill or back), the\n // memoized probe result can go stale and disagree with what's now\n // actually active. Resetting it here means the next locale-aware format()\n // after any setTemporal() call re-probes against the implementation\n // that's active *now*, at the (small, one-time-per-switch) cost of\n // re-running the probe.\n for (const listener of onTemporalChanged) listener();\n}\n\nfunction resolveTemporal(): TemporalNamespace | undefined {\n return injectedTemporal ?? (globalThis as unknown as { Temporal?: TemporalNamespace }).Temporal;\n}\n\nexport function getTemporal(): TemporalNamespace {\n const temporal = resolveTemporal();\n if (!temporal) {\n throw new Error(\n 'temporal-fmt: parse() needs a Temporal implementation to construct its result. ' +\n 'Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal ' +\n '(native on Node 26+, or a polyfill like temporal-polyfill).'\n );\n }\n return temporal;\n}\n","// Structured error classes for parse()/format() failures.\n//\n// As of 0.9.0, every throw site on the parse/format data path —\n// tokenize.ts, pattern.ts, format.ts, parse.ts (parse/safeParse/tryParse/\n// parseToParts/compileParser), and the two data-path throws in\n// localeVocab.ts (partValue/assertNoCollision on the getLocaleVocab side)\n// — throws these typed classes directly instead of plain\n// `new Error(message)`. Every migrated site kept its exact pre-0.9.0\n// message text — subclasses of TemporalFmtError still satisfy\n// `instanceof Error` and message-matching regexes (e.g.\n// `/token \"HH\" requires/`), so this was safe to do without a semver-major\n// bump for either check. What *is* a breaking change for 0.9.0: code that\n// specifically checked `err.constructor === Error` or `err.name ===\n// 'Error'` will see a different name now (e.g. 'FormatSyntaxError'). See\n// the 0.9.0 changelog entry.\n//\n// Not migrated, on purpose:\n// - localeVocab.ts's registration-time throws (assertValidVocab,\n// registerLocaleVocab itself) — these are config-time API-misuse\n// errors on data the developer supplies once at startup, not runtime\n// parse/format failures, so they don't fit this module's\n// TemporalFmtErrorCode taxonomy as-is. assertNoCollision is shared\n// between the registration path and the data path (getLocaleVocab), so\n// its throw stays plain `Error` for both until that split is done —\n// see the tracking note at its call site.\n//\n// wrapUntypedError() below still exists for localeVocab.ts's registration\n// throws and for anything a caller passes into safeParse()/tryParse() from\n// outside this package. On the data path, safeParse's `if (err instanceof\n// TemporalFmtError) return { ok: false, error: err }` now catches every\n// throw before it would reach the classifier, so the regex branches here\n// are effectively dead for parse/format/tokenize/pattern — left in place\n// as the fallback for anything not yet on a typed throw site.\n\nexport type TemporalFmtErrorCode =\n | 'FORMAT_SYNTAX_ERROR'\n | 'UNKNOWN_TOKEN'\n | 'PARSE_MISMATCH'\n | 'INVALID_DATE'\n | 'INVALID_TIME'\n | 'INVALID_OFFSET'\n | 'INVALID_TIME_ZONE'\n | 'INVALID_CALENDAR'\n | 'AMBIGUOUS_INPUT'\n | 'INVALID_LOCALE'\n | 'INVALID_DURATION';\n\nexport interface TemporalFmtErrorFields {\n code: TemporalFmtErrorCode;\n input?: string;\n format?: string;\n token?: string;\n position?: number;\n expected?: string;\n actual?: string;\n reason?: string;\n}\n\n// Base class. `message` is the human-readable summary; the structured\n// fields are the machine-readable parts a linter or codemod reports on.\n// `Error.captureStackTrace` is called manually (where available) so\n// the stack points at the call site, not at this constructor — same\n// pattern Node uses for its own error classes.\nexport class TemporalFmtError extends Error {\n readonly code: TemporalFmtErrorCode;\n readonly input?: string;\n readonly format?: string;\n readonly token?: string;\n readonly position?: number;\n readonly expected?: string;\n readonly actual?: string;\n readonly reason?: string;\n\n constructor(message: string, fields: TemporalFmtErrorFields) {\n super(message);\n this.name = 'TemporalFmtError';\n this.code = fields.code;\n this.input = fields.input;\n this.format = fields.format;\n this.token = fields.token;\n this.position = fields.position;\n this.expected = fields.expected;\n this.actual = fields.actual;\n this.reason = fields.reason;\n // Avoid setting the stack to point at this constructor — keeps it\n // useful. captureStackTrace is V8-only; on other engines the\n // default stack (this constructor line) is what callers get.\n const capture = (Error as unknown as { captureStackTrace?: (target: Error, ctor?: Function) => void }).captureStackTrace;\n if (typeof capture === 'function') {\n capture(this, this.constructor);\n }\n }\n\n // Lets callers do `err.toJSON()` for logging. Plain `Error` doesn't\n // serialize its non-enumerable fields; this picks them up explicitly.\n toJSON(): TemporalFmtErrorFields & { name: string; message: string } {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n input: this.input,\n format: this.format,\n token: this.token,\n position: this.position,\n expected: this.expected,\n actual: this.actual,\n reason: this.reason,\n };\n }\n}\n\n// Each subclass fixes `code` so callers can switch on it without\n// re-checking message text. The constructor takes only the fields\n// that vary per call; `code` and a default `message` template are\n// provided by the subclass.\n\nexport class FormatSyntaxError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `format string \"${fields.format ?? ''}\" has a syntax error${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'FORMAT_SYNTAX_ERROR', ...rest },\n );\n this.name = 'FormatSyntaxError';\n }\n}\n\nexport class UnknownTokenError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `token \"${fields.token ?? ''}\" is not a recognized temporal-fmt token${fields.format ? ` in format string \"${fields.format}\"` : ''}.`,\n { code: 'UNKNOWN_TOKEN', ...rest },\n );\n this.name = 'UnknownTokenError';\n }\n}\n\nexport class ParseMismatchError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not match format \"${fields.format ?? ''}\"${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'PARSE_MISMATCH', ...rest },\n );\n this.name = 'ParseMismatchError';\n }\n}\n\nexport class InvalidDateError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DATE', ...rest },\n );\n this.name = 'InvalidDateError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidTimeError is part of the public\n error-class surface (exported from index.ts, code 'INVALID_TIME')\n but nothing in this package constructs one. Investigated wiring it\n in the same way InvalidTimeZoneError was just wired (see parse.ts's\n zzz-validation loop): checked whether hour/minute/second have a\n post-match semantic range check the way zone ids do. They don't —\n pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce\n their valid ranges at the regex level (e.g. HH is '(?:[01]\\d|2[0-3])',\n which can't match \"99\" in the first place), so an out-of-range time\n is rejected as a plain shape mismatch before any semantic check\n could run. There's no live gap to hook this into without inventing a\n redundant check purely to give this class a body. Left unconstructed\n until the library actually has a real invalid-time case to report. */\nexport class InvalidTimeError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid time${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME', ...rest },\n );\n this.name = 'InvalidTimeError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class InvalidOffsetError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `offset \"${fields.actual ?? ''}\" is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_OFFSET', ...rest },\n );\n this.name = 'InvalidOffsetError';\n }\n}\n\nexport class InvalidTimeZoneError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `time zone \"${fields.actual ?? ''}\" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME_ZONE', ...rest },\n );\n this.name = 'InvalidTimeZoneError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidCalendarError is part of the\n public error-class surface (exported from index.ts, code\n 'INVALID_CALENDAR') but nothing in this package constructs one.\n Checked for a wiring opportunity the same way InvalidTimeZoneError\n got one: there's no user-supplied calendar identifier anywhere in\n the library to validate against a supported list. resolveCalendar()\n in parse.ts derives the calendar entirely from Intl's own resolution\n of the locale string — it's never handed an arbitrary \"calendar\"\n value a caller could get wrong. There's no live input to reject.\n Left unconstructed until the library actually accepts a calendar\n parameter that could be invalid. */\nexport class InvalidCalendarError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `calendar \"${fields.actual ?? ''}\" is not supported${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_CALENDAR', ...rest },\n );\n this.name = 'InvalidCalendarError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class AmbiguousInputError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" is ambiguous${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'AMBIGUOUS_INPUT', ...rest },\n );\n this.name = 'AmbiguousInputError';\n }\n}\n\nexport class InvalidLocaleError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `locale \"${fields.actual ?? ''}\" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_LOCALE', ...rest },\n );\n this.name = 'InvalidLocaleError';\n }\n}\n\nexport class InvalidDurationError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `duration is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DURATION', ...rest },\n );\n this.name = 'InvalidDurationError';\n }\n}\n\n// Wraps a plain Error thrown from a code path that hasn't been\n// migrated to typed errors yet. Carries the original message in\n// `reason` so callers reading the typed surface still see what\n// failed. Used by safeParse() in parse.ts.\n//\n// As of the 0.9.0 migration, every throw site on the parse/format data\n// path throws a TemporalFmtError directly, so safeParse's `instanceof\n// TemporalFmtError` check always passes before this function would be\n// called — nothing in the current test suite reaches any branch below.\n// Kept as the safety net for wrapping a future unmigrated throw site\n// (see the c8-ignored call in parse.ts's safeParse), same reasoning as\n// that call site: removing this would silently drop the \"safeParse\n// always returns a TemporalFmtError\" contract the moment anyone adds a\n// throw new Error(...) without wiring a typed class for it.\n/* c8 ignore start @preserve -- unreachable from the current test suite,\n see rationale above */\nexport function wrapUntypedError(err: Error, context: { input?: string; format?: string }): TemporalFmtError {\n // Try to classify by inspecting the message — covers the existing\n // parse()/format() throw sites without modifying them. Anything\n // that doesn't match a known pattern falls through to a generic\n // ParseMismatchError, which still has the structured fields.\n const msg = err.message;\n if (/unknown token|isn't a recognized token/.test(msg)) {\n return new UnknownTokenError({ input: context.input, format: context.format, reason: msg });\n }\n if (/ambiguous/i.test(msg)) {\n return new AmbiguousInputError({ input: context.input, format: context.format, reason: msg });\n }\n if (/offset/.test(msg) && /out of range|exceeds|doesn't match the shape/i.test(msg)) {\n return new InvalidOffsetError({ input: context.input, format: context.format, reason: msg });\n }\n if (/no valid pattern matches/i.test(msg)) {\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n }\n if (/doesn't describe a valid date\\/time|incomplete date|weekday token|quarter token/.test(msg)) {\n return new InvalidDateError({ input: context.input, format: context.format, reason: msg });\n }\n const lowerMsg = msg.toLowerCase();\n const mentionsLocale = lowerMsg.includes('locale');\n if (\n (mentionsLocale && (lowerMsg.includes('produced no') || lowerMsg.includes('not a valid'))) ||\n lowerMsg.includes('cutoffs must be')\n ) {\n return new InvalidLocaleError({ input: context.input, format: context.format, reason: msg });\n }\n if (/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(msg)) {\n return new FormatSyntaxError({ input: context.input, format: context.format, reason: msg });\n }\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n}\n/* c8 ignore stop @preserve */","// Name lists for the locale-aware tokens (MMMM, MMM, EEEE, EEE, a). Each\n// list 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\nimport { InvalidLocaleError } from './errors.js';\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\n// Custom vocabs registered by callers for locales Intl doesn't cover well\n// (e.g. a 13-month Hebrew leap year, where Intl's 12-month vocabulary\n// silently loses a whole month). Keyed by canonical cache key so the\n// same locale string spelling variants fold together — same convention\n// as the Intl-derived vocab cache above.\nconst customVocabs = new Map<string, LocaleVocab>();\nconst MAX_CUSTOM_VOCABS = 500;\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_ENTRY_LENGTH = 256;\n\nfunction assertValidVocab(vocab: Partial<LocaleVocab>, locale: string): void {\n // Strict shape validation at registration time, not lazily on first\n // use — the README's promise is that a malformed registration throws\n // descriptively here, rather than failing later inside format()/parse()\n // with a confusing \"no month part\" or wrong-month error the caller\n // can't trace back to the bad registration.\n const required: Array<{ key: keyof LocaleVocab; length: number; label: string }> = [\n { key: 'monthLong', length: 12, label: 'long month names' },\n { key: 'monthShort', length: 12, label: 'short month names' },\n { key: 'weekdayLong', length: 7, label: 'long weekday names' },\n { key: 'weekdayShort', length: 7, label: 'short weekday names' },\n { key: 'dayPeriod', length: 2, label: 'day period markers (AM/PM-equivalent)' },\n ];\n\n for (const { key, length, label } of required) {\n const value = vocab[key];\n if (value === undefined) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\" is missing required field \"${key}\" (${label}).`\n );\n }\n if (!Array.isArray(value)) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must be an array, got ${typeof value}.`\n );\n }\n if (value.length !== length) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must have exactly ${length} entries (got ${value.length}) — ${label}.`\n );\n }\n value.forEach((entry, i) => {\n if (typeof entry !== 'string' || entry.length === 0) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string, got ${String(entry)}.`\n );\n }\n if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {\n throw new RangeError(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`\n );\n }\n });\n }\n\n // Reuse the same collision check the Intl-derived path uses — a\n // duplicate month name is just as ambiguous when supplied by a caller\n // as when produced by Intl.\n assertNoCollision(vocab.monthLong!, 'MMMM month', locale);\n assertNoCollision(vocab.monthShort!, 'MMM month', locale);\n assertNoCollision(vocab.weekdayLong!, 'EEEE weekday', locale);\n assertNoCollision(vocab.weekdayShort!, 'EEE weekday', locale);\n\n // dayPeriod entries must differ from each other, or parse()'s\n // isPM check (raw === vocab.dayPeriod[1]) can never return true and\n // every 12-hour parse silently resolves to AM. The Intl-derived path\n // dedupes a same-AM/PM collision to length 1, but a caller passing\n // both entries identical is a real bug to surface — not something to\n // dedupe around.\n if (vocab.dayPeriod![0] === vocab.dayPeriod![1]) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": dayPeriod entries must differ ` +\n `(both are \"${vocab.dayPeriod![0]}\"); otherwise parse() can't tell AM from PM.`\n );\n }\n}\n\n// Anything that caches a result derived from *which* vocabulary is\n// active for a locale (right now: parse.ts's compiled pattern cache,\n// whose MMMM/MMM/EEEE/EEE/a fragments embed the vocab's alternations)\n// subscribes here so it gets invalidated when a registration swaps the\n// vocab — mirrors temporalProvider.ts's subscribeToTemporalChanges.\nconst onVocabChanged: Array<() => void> = [];\n\nexport function subscribeToVocabChanges(listener: () => void): void {\n onVocabChanged.push(listener);\n}\n\n/**\n * Supply a custom month/weekday/day-period vocabulary for a locale key,\n * overriding the Intl-derived vocab this library would otherwise build\n * for that key. Useful for locales Intl doesn't cover well — the README's\n * known-limitations section calls out the Hebrew leap-month gap as a\n * specific case this addresses.\n *\n * Throws descriptively on malformed input (wrong array lengths, empty\n * strings, duplicate entries, missing fields) rather than failing later\n * during format/parse.\n *\n * Registered vocab takes precedence over the Intl-derived vocab for that\n * locale key, including for already-cached entries — registering\n * invalidates the prior cache entry for that locale so the next call\n * picks up the new vocab.\n *\n * @example\n * registerLocaleVocab('en-u-ca-hebrew-leap', {\n * monthLong: ['Nisan','Iyar','Sivan','Tammuz','Av','Elul','Tishrei','Marcheshvan','Kislev','Tevet','Shevat','Adar I','Adar II'],\n * monthShort: ['Nis','Iyy','Siv','Tam','Av','Elu','Tish','Chesh','Kis','Tev','Shv','Ad1','Ad2'],\n * weekdayLong: ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'],\n * weekdayShort: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],\n * dayPeriod: ['AM','PM'],\n * });\n */\nexport function registerLocaleVocab(locale: string, vocab: Partial<LocaleVocab>): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);\n }\n assertValidVocab(vocab, locale);\n\n const cacheKey = canonicalCacheKey(locale);\n if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);\n }\n customVocabs.set(cacheKey, {\n monthLong: [...vocab.monthLong!],\n monthShort: [...vocab.monthShort!],\n weekdayLong: [...vocab.weekdayLong!],\n weekdayShort: [...vocab.weekdayShort!],\n dayPeriod: [...vocab.dayPeriod!],\n });\n // Invalidate the Intl-derived cache entry so any prior format/parse\n // result cached for this locale is rebuilt against the new vocab. Not\n // strictly necessary (getLocaleVocab checks customVocabs first), but\n // cheap and keeps the two caches from drifting out of sync.\n vocabCache.delete(cacheKey);\n // Also invalidate downstream caches that baked the previous vocab in\n // at build time (parse.ts's pattern cache is the one that matters:\n // without this, a cached pattern would keep matching the OLD month /\n // weekday names while format() renders the new ones, so the library's\n // own format() output would fail to parse back).\n for (const listener of onVocabChanged) listener();\n}\n\n// Every locale-keyed cache in this library (this one, formatterCache in\n// tokens.ts, patternCache/calendarCache in parse.ts) used to key on the\n// exact locale string a caller passed in. Intl treats spelling variants of\n// the same locale as equivalent ('en-US' / 'en-us' / 'en_US' all resolve\n// the same way), but a plain string-keyed Map doesn't — so callers mixing\n// spellings for what's really one locale would silently fragment across\n// separate cache entries instead of sharing one, making the bounded\n// eviction limits (MAX_*_CACHE_SIZE) less effective than they look. This\n// doesn't change any cache's *correctness* (each entry is still built from\n// -- and valid for -- whatever locale string produced it), only how many\n// distinct entries equivalent spellings end up costing. Falls back to the\n// original string on a malformed/unrecognized tag rather than throwing —\n// cache-key normalization shouldn't be where a bad locale first surfaces\n// as an error; whatever actually calls `new Intl.DateTimeFormat(locale)`\n// downstream is the right place for that.\n// Intl constructors (DateTimeFormat/NumberFormat/RelativeTimeFormat)\n// reject underscore-separated tags like 'en_US' outright, while\n// canonicalCacheKey and every locale-parsing path in this library\n// tolerates them by normalizing to BCP-47 hyphens. Centralize that\n// normalization so every `new Intl.*(locale)` construction site\n// accepts the same spellings the cache keys do. Genuinely malformed\n// tags still throw downstream, unchanged.\nexport function normalizeLocaleTag(locale: string): string {\n return locale.replace(/_/g, '-');\n}\n\n// Memoized so hot paths (parse()'s resolveCalendar keys off this on\n// every call, and every locale-keyed cache re-derives it) don't build a\n// fresh Intl.Locale per invocation — construction is comparatively\n// expensive. Bounded like every other cache in this library.\nconst canonicalKeyCache = new Map<string, string>();\nconst MAX_CANONICAL_KEY_CACHE = 500;\n\nexport function canonicalCacheKey(locale: string): string {\n const hit = canonicalKeyCache.get(locale);\n if (hit !== undefined) return hit;\n let key: string;\n try {\n // Intl.Locale requires BCP-47 hyphens and rejects underscore-separated\n // tags like 'en_US' outright (RangeError), rather than normalizing\n // them — so without this replace, that spelling would just fall\n // through to the catch below and never fold with 'en-US'.\n key = new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n // Malformed tag: key on the raw string. Callers that must reject\n // malformed tags do it via assertValidLocaleTag() before/instead of\n // relying on this function — cache-key normalization isn't where a\n // bad locale should surface as an error (unchanged behavior).\n key = locale;\n }\n if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {\n const oldestKey = canonicalKeyCache.keys().next().value;\n if (oldestKey !== undefined) canonicalKeyCache.delete(oldestKey);\n }\n canonicalKeyCache.set(locale, key);\n return key;\n}\n\n// Single validation choke point for locale tags at public boundaries.\n// Intl constructors reject malformed tags with a bare engine RangeError\n// (\"Incorrect locale information provided\") that carries none of the\n// library's structured error context; this rethrows as the typed\n// InvalidLocaleError with the offending tag in its fields. Accepts the\n// same spellings as canonicalCacheKey (underscore tags normalized).\nexport function assertValidLocaleTag(locale: string): void {\n try {\n new Intl.Locale(normalizeLocaleTag(locale));\n } catch {\n throw new InvalidLocaleError({\n actual: locale,\n reason: 'not a valid BCP-47 locale tag',\n });\n }\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\nconst MAX_VOCAB_CACHE_SIZE = 500;\n\n// Some locales (ja-JP) split a field across two parts — month \"8\" plus a\n// counter suffix \"月\" as a separate sibling \"literal\" — while format()'s\n// tokens go through toLocaleString(), which concatenates everything into\n// \"8月\". Reading only the type-tagged part used to drop that suffix, so\n// this locale's vocab never matched what format() actually produced.\n// Only merges *adjacent* literals, not the whole string, since the\n// dayPeriod/weekday formatters below carry an extra hour part that a\n// join-everything approach would wrongly absorb.\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const parts = formatter.formatToParts(date);\n const index = parts.findIndex((p) => p.type === type);\n /* c8 ignore start @preserve -- defensive guard against a real but\n unreproducible failure mode: an Intl implementation that omits the\n requested part type entirely for some locale. Checked every locale\n with unusual dayPeriod/weekday/month rendering available in this\n runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)\n against the exact formatter options getLocaleVocab uses (notably\n hour12: true for the dayPeriod formatter, which is what makes every\n locale here actually emit a dayPeriod part — omitting it is what\n produced a false \"gap\" during investigation). None omit their part\n on this runtime. A different ICU version or a non-Node Intl\n implementation could plausibly behave differently, so this stays a\n real check rather than an assertion. */\n /* c8 ignore start @preserve */\n if (index === -1) {\n throw new InvalidLocaleError({\n message: `temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`,\n });\n }\n /* c8 ignore stop @preserve */\n let value = parts[index]!.value;\n const prev = parts[index - 1];\n const next = parts[index + 1];\n // skip whitespace literals (the separator before \"AM\") — only a\n // no-space suffix like ja-JP's \"月\" should get folded in\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\n// Two entries rendering identically means parse()'s reverse lookup\n// (indexOf) can never tell them apart. Weekday collisions already surface\n// via parse()'s dayOfWeek cross-check, but with a confusing same-string\n// error; months have no equivalent cross-check, so a collision there would\n// otherwise resolve silently to the wrong month. Catching both here, once\n// at build time, gives one clear error instead.\nfunction assertNoCollision(names: string[], label: string, locale: string): void {\n const seen = new Map<string, number>();\n for (let i = 0; i < names.length; i++) {\n const prior = seen.get(names[i]!);\n if (prior !== undefined) {\n // Not migrated to a typed error: this function is shared between\n // getLocaleVocab's Intl-derived path (data-path error, would be a\n // good InvalidLocaleError candidate) and assertValidVocab's\n // registration-time check (out of scope for this pass — see the\n // localeVocab.ts registration-error follow-up). Splitting this into\n // two near-duplicate functions just to route error types\n // differently isn't worth it for one throw; revisit together with\n // the registration-error work instead.\n throw new Error(\n `temporal-fmt: locale \"${locale}\" renders ${label} index ${prior} and ${i} identically ` +\n `(\"${names[i]}\"). parse() can't reliably tell these apart for this locale/token, so this ` +\n `combination isn't supported.`\n );\n }\n seen.set(names[i]!, i);\n }\n}\n\n// Exposed for tokens.ts: when a custom vocab is registered for this\n// locale, format()'s locale-aware tokens (MMMM/MMM/EEEE/EEE/a) read\n// straight from the registered array instead of going through\n// Intl.DateTimeFormat. Without this override, format() would silently\n// keep producing Intl's strings while parse() matched against the\n// registered vocab — the two would round-trip-fail.\nexport function getCustomVocab(locale: string): LocaleVocab | undefined {\n const cacheKey = canonicalCacheKey(locale);\n return customVocabs.get(cacheKey);\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n // Validate before building vocab: a malformed tag used to surface as a\n // raw RangeError from the first new Intl.DateTimeFormat below.\n assertValidLocaleTag(locale);\n const cacheKey = canonicalCacheKey(locale);\n // Registered vocabs take precedence over the Intl-derived one — this\n // is the override mechanism registerLocaleVocab() promises. Checking\n // here, before the Intl cache lookup, means a registration that\n // happens *after* the first Intl-derived vocab was built still takes\n // effect on the next call.\n const custom = customVocabs.get(cacheKey);\n if (custom) {\n return custom;\n }\n const cached = vocabCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const intlLocale = normalizeLocaleTag(locale);\n const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { 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 assertNoCollision(monthLong, 'MMMM month', locale);\n assertNoCollision(monthShort, 'MMM month', locale);\n\n const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { 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 // redundant with parse()'s dayOfWeek cross-check, but gives a clearer error\n assertNoCollision(weekdayLong, 'EEEE weekday', locale);\n assertNoCollision(weekdayShort, 'EEE weekday', locale);\n\n const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { 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 if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {\n const oldestKey = vocabCache.keys().next().value;\n if (oldestKey !== undefined) vocabCache.delete(oldestKey);\n }\n vocabCache.set(cacheKey, vocab);\n return vocab;\n}","// ISO 8601 week numbering: a week runs Monday–Sunday, and week 1 of a year\n// is the week containing the year's first Thursday (equivalently, the week\n// containing January 4). This means late-December dates can fall in week 1\n// of the *next* year, and early-January dates can fall in week 52 or 53 of\n// the *previous* year. The \"ISO week-numbering year\" (what `RRRR` formats)\n// is that adjacent year, not the calendar year.\n//\n// Computed here from the date's own year/month/day plus its ISO dayOfWeek\n// (1=Mon..7=Sun, matching Temporal's numbering) using plain Gregorian\n// arithmetic — no Temporal factory needed. format() only has the fields\n// the caller already put on the object, and requiring a Temporal\n// implementation just for ISO week would be a regression for callers who\n// use format() without setTemporal() on a non-26 Node.\n\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\nexport function isGregorianLeapYear(year: number): boolean {\n // Gregorian rule: divisible by 4, except centuries which must also be\n // divisible by 400. Temporal's iso8601 calendar is proleptic Gregorian\n // (no Julian cutover), so this applies for every year, including BCE.\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nfunction daysInYear(year: number): 365 | 366 {\n return isGregorianLeapYear(year) ? 366 : 365;\n}\n\nexport function dayOfYear(year: number, month: number, day: number): number {\n let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1]! + day;\n if (month > 2 && isGregorianLeapYear(year)) doy += 1;\n return doy;\n}\n\n// Jan 1, 2000 was a Saturday — ISO dayOfWeek 6. Anchoring day-of-week\n// computations to a known reference date is simpler and cheaper than\n// pulling in Zeller's congruence, and the reference never changes.\nconst REFERENCE_YEAR = 2000;\nconst REFERENCE_JAN1_DAY_OF_WEEK = 6;\n\nfunction dayOfWeekOfJan1(year: number): number {\n // Sum full-year deltas from the 2000 anchor rather than recomputing from\n // scratch each call — the per-call work is a single mod this way, and\n // the loop rarely runs far (a typical caller passes a current-era year).\n let offset = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);\n }\n // Convert \"days since Jan 1, 2000\" into an ISO day-of-week (1=Mon..7=Sun).\n // Jan 1, 2000 was ISO 6 (Sat), so zero-indexed dow = (6-1 + offset) mod 7.\n const zeroIndexed = (((6 - 1 + offset) % 7) + 7) % 7;\n return zeroIndexed + 1;\n}\n\nexport interface IsoWeekDate {\n isoYear: number;\n week: number; // 1..53\n}\n\nexport function isoWeekYearAndWeek(year: number, month: number, day: number, dayOfWeek: number): IsoWeekDate {\n // Step 1: find the Thursday of the current ISO week. The ISO week-numbering\n // year is whichever calendar year that Thursday falls in. Computing it via\n // day-of-year offsets (rather than constructing a Temporal.PlainDate and\n // adding days) keeps this function pure-numeric.\n const doy = dayOfYear(year, month, day);\n const thursdayDoyRelative = doy + (4 - dayOfWeek); // may be <1 or >daysInYear\n\n let isoYear: number;\n let thursdayDoy: number;\n if (thursdayDoyRelative < 1) {\n isoYear = year - 1;\n thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);\n } else if (thursdayDoyRelative > daysInYear(year)) {\n isoYear = year + 1;\n thursdayDoy = thursdayDoyRelative - daysInYear(year);\n } else {\n isoYear = year;\n thursdayDoy = thursdayDoyRelative;\n }\n\n // Step 2: locate the first Thursday of isoYear — its week is week 1. The\n // first Thursday's day-of-year depends on what weekday Jan 1 of isoYear is.\n const jan1Dow = dayOfWeekOfJan1(isoYear);\n const firstThursdayDoy = 1 + ((4 - jan1Dow + 7) % 7); // 1..7\n\n // Step 3: count full weeks between the two Thursdays.\n const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);\n return { isoYear, week };\n}\n","import { getTemporal, subscribeToTemporalChanges } from './temporalProvider.js';\nimport { canonicalCacheKey, getCustomVocab, normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\nimport { isoWeekYearAndWeek, dayOfYear } from './isoWeek.js';\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 — always `±HH:MM` (6 chars) for any\n // modern date. Historical LMT offsets with seconds exist in IANA but\n // aren't reachable through this Temporal field, so format-side offset\n // tokens can assume the canonical 6-char shape. Parse-side writes a\n // canonicalized `+HH:MM` here before handing it to\n // Temporal.ZonedDateTime.from as the `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\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 // 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 // offset is always 6 chars: sign + HH + ':' + MM\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 ['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 const { week } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(week, 2);\n }, 'dayOfWeek'],\n ['RRRR', (t) => {\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) => String(dayOfYear(t.year!, t.month!, t.day!)), 'day'],\n ['DD', (t) => pad(dayOfYear(t.year!, t.month!, t.day!), 2), 'day'],\n ['DDD', (t) => pad(dayOfYear(t.year!, t.month!, t.day!), 3), '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];","import { TOKENS } from './tokens.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new FormatSyntaxError({\n format,\n message: `temporal-fmt: unterminated quote in format string \"${format}\"`,\n });\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // match is already the longest registered token starting at i, so\n // one more of its last character can't be a token of its own —\n // it'd fall through to whatever token or literal rule handles that\n // character next, silently splicing an unrelated field onto this\n // one (e.g. \"zzzz\" used to read as zzz + literal \"z\", \"MMMMM\" as\n // MMMM + the numeric-month token M). Treat the whole overlong run\n // as one unrecognized token instead.\n const runChar = match[match.length - 1];\n if (format[i + match.length] === runChar) {\n let end = i + match.length;\n while (format[end] === runChar) end += 1;\n // UnknownTokenError, not FormatSyntaxError: wrapUntypedError's\n // classifier already treats this exact message (\"isn't a\n // recognized token\") as UNKNOWN_TOKEN (see errors.test.js's\n // \"overlong token run classifies as UnknownTokenError\"), and a\n // direct throw here needs to agree with what safeParse's fallback\n // path would have classified it as.\n throw new UnknownTokenError({\n format,\n token: format.slice(i, end),\n message:\n `temporal-fmt: \"${format.slice(i, end)}\" in format string \"${format}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is\n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}","// Numbering systems. Default: latn (ASCII digits).\n// Optional: arab, deva, beng, etc. Configurable on formatting.\n//\n// The library's existing tokens always render ASCII digits (see\n// tokens.ts's pad() — uses String(n) which produces ASCII). This\n// module adds the ability to convert the output to a locale's native\n// digits via Intl.NumberFormat, which is the standard mechanism JS\n// provides for digit transliteration.\n//\n// On the parse side: parse() only accepts ASCII digits, matching how\n// the existing NUMERIC_FRAGMENTS regex is built. A parseNumberingSystem\n// option could convert input digits to ASCII before matching — but\n// that's a per-call opt-in, not silent acceptance of every numeral\n// system.\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport type NumberingSystem = 'latn' | 'arab' | 'deva' | 'beng' | 'guru' | 'gujr' | 'orya' | 'tamldec' | 'telu' | 'knda' | 'mlym' | 'fullwide' | 'hanidec';\n\n// All supported NumberingSystem values. latn is the default and what\n// the rest of the library produces natively.\nexport const SUPPORTED_NUMBERING_SYSTEMS: ReadonlySet<string> = new Set([\n 'latn', 'arab', 'deva', 'beng', 'guru', 'gujr', 'orya', 'tamldec',\n 'telu', 'knda', 'mlym', 'fullwide', 'hanidec',\n]);\n\nconst digitMapCache = new Map<string, Record<string, string>>();\n\n// Builds a per-numbering-system digit transliteration map. Uses\n// Intl.NumberFormat to render 0-9 in the requested system, then\n// builds the lookup table. Cached because constructing a formatter\n// is expensive and we re-use the same map for every digit in the\n// output.\nfunction getDigitMap(system: string): Record<string, string> {\n let map = digitMapCache.get(system);\n if (map) return map;\n /* c8 ignore start @preserve -- dead by construction, not just\n untested: both callers (convertDigits, convertDigitsToAscii) already\n return early on system === 'latn' before calling getDigitMap at all,\n so this function is never invoked with 'latn'. Kept as a defensive\n fallback rather than trusting that stays true for any future\n caller. */\n if (system === 'latn') {\n map = {};\n for (let i = 0; i < 10; i++) map[String(i)] = String(i);\n } else {\n /* c8 ignore stop @preserve */\n const fmt = new Intl.NumberFormat('en-US-u-nu-' + system, { useGrouping: false });\n map = {};\n for (let i = 0; i < 10; i++) {\n map[String(i)] = fmt.format(i);\n }\n }\n digitMapCache.set(system, map);\n return map;\n}\n\n// Converts every ASCII digit in `s` to its equivalent in the requested\n// numbering system. Non-digit characters pass through unchanged.\nexport function convertDigits(s: string, system: string): string {\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(', ')}.` });\n }\n const map = getDigitMap(system);\n let result = '';\n for (const ch of s) {\n if (ch >= '0' && ch <= '9') {\n // map[ch] is always populated for '0'-'9' since getDigitMap builds\n // all ten digit keys for every supported system and ch is already\n // range-checked above; the ?? ch fallback exists only as a\n // type-safety guard against Record's implicit undefined, not a\n // reachable runtime path.\n /* c8 ignore next */\n result += map[ch] ?? ch;\n } else {\n result += ch;\n }\n }\n return result;\n}\n\n// Parses a string with non-ASCII digits back to ASCII. The inverse of\n// convertDigits — used by parse() when an explicit `numberingSystem`\n// option is passed. Throws on unsupported systems.\nexport function convertDigitsToAscii(s: string, system: string): string {\n // Same dead-by-construction situation as applyParseNumbering's own\n // 'latn' guard: this function's only caller (applyParseNumbering)\n // already returns early on system === 'latn' before reaching this\n // call, so system is never 'latn' here in practice. Kept as a\n // defensive guard for any future direct caller.\n /* c8 ignore next */\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported.` });\n }\n const map = getDigitMap(system);\n // Build reverse map.\n const reverse: Record<string, string> = {};\n for (const k of Object.keys(map)) reverse[map[k]!] = k;\n let result = '';\n for (const ch of s) {\n result += reverse[ch] ?? ch;\n }\n return result;\n}\n\n// Augmented FormatOptions that includes the numberingSystem field.\n// Callers pass { numberingSystem: 'arab' } to format() to get Arabic-\n// Indic digits in the output.\nexport interface NumberingFormatOptions extends FormatOptions {\n numberingSystem?: string;\n}\n\n// Parse-side counterpart. Named parseNumberingSystem (not numberingSystem)\n// so a caller mixing format() and parse() options in the same config\n// object can set both independently — the two directions aren't always\n// symmetric (e.g. converting *to* native digits on output without\n// expecting native digits back on input, or vice versa).\nexport interface NumberingParseOptions extends FormatOptions {\n parseNumberingSystem?: string;\n}\n\n// Helper for the format path: takes the formatted ASCII output of\n// format() and converts digits if a numberingSystem was requested.\n// Kept here so format.ts doesn't need to know about numbering systems.\nexport function applyNumbering(s: string, options: NumberingFormatOptions): string {\n const system = options.numberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigits(s, system);\n}\n\n// Helper for the parse path: converts input digits to ASCII before\n// matching, when parseNumberingSystem is set. Distinct from the format\n// path's option name (numberingSystem vs parseNumberingSystem) so a\n// caller can be explicit about which direction they want transliterated.\nexport function applyParseNumbering(s: string, options: { parseNumberingSystem?: string }): string {\n // Both call sites of applyParseNumbering (parse.ts) already guard with\n // `if (options.parseNumberingSystem)` before calling it, so\n // options.parseNumberingSystem is always truthy here — the ?? 'latn'\n // fallback, and the subsequent 'latn' early return, are dead by\n // construction, not just untested. Kept as a defensive default rather\n // than assuming every future caller replicates the guard.\n /* c8 ignore next 2 */\n const system = options.parseNumberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigitsToAscii(s, system);\n}\n\n// Suppress unused-import warning. DEFAULT_LOCALE is imported for the\n// type augmentation pattern above; if the project ever exports a\n// typed-locale interface, it'll be the source of truth for the default.\nvoid DEFAULT_LOCALE;","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize, type Piece } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { applyNumbering, type NumberingFormatOptions } from './numbering.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n// Pre-tokenized format strings, keyed by (formatStr) — locale doesn't\n// change the tokenization step, only the per-token rendering, so the\n// piece list is shared across locales. Same eviction shape as the\n// other caches in this library.\nconst tokenizeCache = new Map<string, Piece[]>();\nconst MAX_TOKENIZE_CACHE_SIZE = 500;\n\nfunction getPieces(formatStr: string): Piece[] {\n let pieces = tokenizeCache.get(formatStr);\n if (pieces) return pieces;\n if (tokenizeCache.size >= MAX_TOKENIZE_CACHE_SIZE) {\n const oldestKey = tokenizeCache.keys().next().value;\n if (oldestKey !== undefined) tokenizeCache.delete(oldestKey);\n }\n pieces = tokenize(formatStr);\n tokenizeCache.set(formatStr, pieces);\n return pieces;\n}\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 *\n * Throws on a token the input type doesn't support (e.g. 'HH' on a PlainDate).\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\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 = getPieces(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 /* c8 ignore start @preserve -- unreachable: tokenize() only ever emits\n tokens present in TOKENS, and HANDLER_BY_TOKEN is built from that\n same list, so every token piece.value can hold already has an entry\n here. Same defensive-guard category as the twin checks in\n analyze.ts/formatDuration.ts. */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 // Numeral transliteration happens last and only on request — every\n // upstream token handler still emits plain ASCII digits, so this is\n // the single place output digits can diverge from that default.\n return applyNumbering(result, options);\n}\n\n// Shape mirrors Intl.DateTimeFormat.formatToParts: each entry is either\n// a literal (carrying no token info) or a token piece (carrying the\n// token string and the formatted value). Letting callers iterate parts\n// means they can build custom output — strip a token, swap a separator,\n// render each token to its own DOM node — without re-implementing the\n// tokenizer or the field-check logic.\nexport interface FormattedPart {\n type: 'literal' | 'token';\n value: string;\n // Present when `type === 'token'`. Carries the token string (e.g. \"yyyy\")\n // so a caller can look up its metadata via tokenInfo() from analyze.ts.\n token?: string;\n}\n\nexport function formatToParts(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): FormattedPart[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = getPieces(formatStr);\n const result: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n // Collapse adjacent literals so formatToParts stays consistent with\n // how format() walks pieces — a multi-char literal \"at \" is one\n // entry, not one per character. Same merge logic as appendLiteral\n // in tokenize.ts.\n const last = result[result.length - 1];\n /* c8 ignore start @preserve -- unreachable through tokenize()'s own\n output: appendLiteral() in tokenize.ts already merges every\n adjacent literal character before pieces ever reaches here, so\n two 'literal' pieces never sit back-to-back in the array this\n loop walks. Kept as defense-in-depth in case that invariant ever\n changes upstream. */\n if (last && last.type === 'literal') {\n last.value += piece.value;\n } else {\n /* c8 ignore stop @preserve */\n result.push({ type: 'literal', value: piece.value });\n }\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the note in format() above */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 // Numeral transliteration applies per-token-part here, rather than\n // once at the end like format() does, so a caller styling individual\n // parts (e.g. one <span> per token) still gets correctly-transliterated\n // digits in each part instead of plain ASCII.\n result.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return result;\n}\n\n// Pre-compiles a format string into an object whose format()/formatToParts()\n// methods skip the tokenization step on every call. The tokenizeCache in\n// this module means a plain format(temporal, fmt) call already pays only\n// a Map lookup for tokenization after the first call, so compileFormat()\n// is mostly a typing/ergonomics affordance — useful for callers who want\n// to hold onto a compiled form explicitly (e.g. to inspect the pieces\n// via the .pieces property, or to pass the compiled object around\n// instead of the string).\nexport interface CompiledFormat {\n format(temporal: TemporalLike, options?: NumberingFormatOptions): string;\n formatToParts(temporal: TemporalLike, options?: NumberingFormatOptions): FormattedPart[];\n readonly pieces: ReadonlyArray<Piece>;\n readonly formatStr: string;\n}\n\nexport function compileFormat(formatStr: string): CompiledFormat {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n // Pre-tokenize once. Validation (unknown tokens, unterminated quotes)\n // happens here, not lazily on first format() call — surfaces a bad\n // format string at compile time rather than at first use, which is\n // the point of compiling up front.\n const pieces = getPieces(formatStr);\n return {\n formatStr,\n pieces,\n format(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 result += handler.fn(temporal, locale);\n }\n return applyNumbering(result, options);\n },\n formatToParts(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore next -- unreachable, see the note in formatToParts() above */\n if (last && last.type === 'literal') last.value += piece.value;\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 out.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return out;\n },\n };\n}\n\n// Exported so analyze.ts can reuse the same tokenization cache rather\n// than re-tokenizing when a caller asks for both a format() and an\n// analyzeFormat() on the same string.\nexport function _getPieces(formatStr: string): Piece[] {\n return getPieces(formatStr);\n}\n\n// Exported for analyze.ts — same reason as above. The handler map is\n// the source of truth for \"what field does this token need\" — analyze\n// consumes it to compute requiredFields and compatibleTypes.\nexport function _handlerFor(token: string): { fn: (t: TemporalLike, locale: string) => string; field: keyof TemporalLike } | undefined {\n return HANDLER_BY_TOKEN.get(token);\n}","import { getLocaleVocab } from './localeVocab.js';\nimport { UnknownTokenError } from './errors.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[], caseInsensitive = false): string {\n const escaped = values.map(escapeRegExp);\n if (!caseInsensitive) return `(?:${escaped.join('|')})`;\n // JS regex has no per-group inline case-insensitive flag, and this\n // fragment gets embedded in one larger pattern built with a single flag\n // set — so case-folding here means listing both cases explicitly rather\n // than relying on a flag.\n return `(?:${escaped.map(foldCase).join('|')})`;\n}\n\n// Expands \"PM\" into a character-class-per-letter pattern matching any\n// casing of it (\"[Pp][Mm]\"), so \"pm\", \"Pm\", \"PM\" all match the same\n// alternative. Only used for the day-period token (see the 'a' case\n// below) — not applied to month/weekday names, where case-folding across\n// scripts is a different and riskier problem this doesn't need to solve.\nfunction foldCase(value: string): string {\n return value.replace(/[a-zA-Z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);\n}\n\n// Every real IANA zone id is letters/digits/'_'/'+'/'-' segments joined by\n// '/' (e.g. \"America/Argentina/Buenos_Aires\", \"Etc/GMT+12\"); UTC and\n// fixed-offset strings are the only other shapes zzz accepts. Matching that\n// *shape* here — instead of alternating all ~400 zone names inline — keeps\n// the compiled regex small regardless of how many zzz tokens appear in a\n// format string. The captured text still gets checked against the real\n// zone set in isValidTimeZone() after the overall pattern matches, so this\n// is strictly a matching-cost change, not a validation-strictness change:\n// a bogus zone id fails \"no valid pattern matches\" exactly like it did when\n// the zone list was inlined (see isValidTimeZone's caller in parse.ts).\nconst TIME_ZONE_SHAPE = '(?:UTC|[+-]\\\\d{2}:\\\\d{2}(?::\\\\d{2}(?:\\\\.\\\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\\\d{1,2})?(?:\\\\/[A-Za-z0-9_+-]+)*)';\n\n// Per-variant regex shapes for the six offset tokens. Each matches the\n// shape its own format counterpart produces, so a round-trip\n// format->parse succeeds for any input the library itself emitted.\n//\n// Kept loose (no hour/minute range bounds inline) on purpose, mirroring\n// how TIME_ZONE_SHAPE is loose: a permissive shape here lets parse()\n// surface a descriptive out-of-range error post-match (see\n// parseOffsetString in parse.ts) instead of the generic \"no valid pattern\n// matches\" the regex throws when the shape itself fails. \"+99:99\" should\n// tell the user it's out of range, not look like the input never matched\n// the format at all.\n//\n// X / x accept an optional minutes group so whole-hour offsets can be\n// written short (\"+05\") while non-whole-hour offsets still parse\n// (\"+0530\"). The optional group is greedy, so for input \"+0530\" the\n// engine prefers the 4-digit match; only falls back to 2-digit when\n// there's nothing else to consume — same longer-first preference the\n// unpadded numeric tokens use elsewhere in this file (see the comment on\n// NUMERIC_FRAGMENTS).\nconst OFFSET_SHAPES: Record<string, string> = {\n X: '(?:Z|[+-]\\\\d{2}(?:\\\\d{2})?)',\n XX: '(?:Z|[+-]\\\\d{4})',\n XXX: '(?:Z|[+-]\\\\d{2}:\\\\d{2})',\n x: '[+-]\\\\d{2}(?:\\\\d{2})?',\n xx: '[+-]\\\\d{4}',\n xxx: '[+-]\\\\d{2}:\\\\d{2}',\n};\n\nfunction getTimeZoneFragment(): string {\n return TIME_ZONE_SHAPE;\n}\n\nlet validZoneSet: Set<string> | undefined;\n\n// Real zone ids plus the couple of aliases zzz has always accepted even\n// though Intl.supportedValuesOf('timeZone') doesn't list them (UTC isn't\n// itself an IANA zone name, it's the identity offset).\nfunction getValidZoneSet(): Set<string> {\n if (!validZoneSet) {\n validZoneSet = new Set(Intl.supportedValuesOf('timeZone'));\n validZoneSet.add('UTC');\n }\n return validZoneSet;\n}\n\nconst FIXED_OFFSET_RE = /^[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?$/;\n\n// Called post-match on whatever the bounded TIME_ZONE_SHAPE captured, since\n// that shape is deliberately looser than \"a real zone id\" (it has to be, to\n// stay a fixed-size regex fragment — see the comment above). A fixed offset\n// is valid by construction; anything else has to be a real IANA name.\nexport function isValidTimeZone(raw: string): boolean {\n return FIXED_OFFSET_RE.test(raw) || getValidZoneSet().has(raw);\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\n//\n// Unpadded alternatives (M, H, h, m, s) list the longer branch first\n// (e.g. '1[0-2]|[1-9]', not '[1-9]|1[0-2]'). This matters only when two\n// unpadded tokens are glued with no separator: with short-first ordering,\n// a regex engine takes the first successful overall match, and won't\n// backtrack into a token's second alternative unless its first choice\n// makes the *rest* of the pattern fail outright. If the short reading also\n// happens to leave a valid match for the next token, the engine stops\n// there — silently, deterministically, and with no relation to which\n// reading a human intended. E.g. \"Md\" against \"121\": short-first order\n// resolves it as month=1/day=21 (M grabs '1', d gets '21', which is a\n// valid day) instead of month=12/day=1. Longer-first ordering fixes this\n// by making the greedy match try to consume as many digits as possible\n// before ever handing digits to the next token, which is the reading\n// that matches how format() itself produces glued output in the first\n// place (format() always emits the token's natural width, so decoding\n// should prefer the same). Found via the token×token combinatorial glue\n// matrix in combinatorial.test.js — see that file for the full case list.\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:1[0-2]|[1-9])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[12]\\\\d|3[01]|[1-9])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:1\\\\d|2[0-3]|[0-9])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:1[0-2]|[1-9])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[1-5]\\\\d|[0-9])',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[1-5]\\\\d|[0-9])',\n SSSSSSSSS: '\\\\d{9}',\n SSSSSSSS: '\\\\d{8}',\n SSSSSSS: '\\\\d{7}',\n SSSSSS: '\\\\d{6}',\n SSSSS: '\\\\d{5}',\n SSSS: '\\\\d{4}',\n SSS: '\\\\d{3}',\n SS: '\\\\d{2}',\n S: '\\\\d',\n // Q is always a single digit 1-4 (no padding variant, so no width ambiguity\n // with adjacent digit tokens the way M/d/H/m/s have).\n Q: '[1-4]',\n};\n\n// QQQ matches its own formatted output: the literal \"Q\" prefix plus a single\n// digit 1-4. Kept as a static fragment rather than routed through vocab,\n// since the \"Q\" prefix is part of the token's own contract, not locale-vocab\n// data that could ever differ.\nconst QQQ_FRAGMENT = 'Q[1-4]';\n\n// Format-only tokens — they have no parse counterpart. The tokenizer still\n// recognizes them (so format() can use them), but parse()'s regex builder\n// rejects them with a clear error rather than silently dropping the token\n// or falling through to the generic \"unknown token\" message.\n// Exported so analyze.ts can include them in the public analyzer surface.\nexport const FORMAT_ONLY_TOKENS = new Set(['do', 'ww', 'RRRR', 'D', 'DD', 'DDD', 'LLLL', 'LLL', 'cccc', 'ccc', 'GGGG', 'G', 'zzzz', 'z']);\n\n// pad()'s year formatter (tokens.ts) never truncates: it preserves the sign\n// for BCE years and doesn't cap width past 9999, so a formatted \"yyyy\" can\n// be longer than 4 digits or start with '-'. YYYY_EXTENDED accepts that;\n// YYYY_EXACT is the plain 4-unsigned-digit case. Which one a given \"yyyy\"\n// occurrence gets depends on what follows it — see buildCapturingPattern in\n// parsePattern.ts. Two separate fragments instead of one `-?\\d{4,}` because\n// an open-ended-width year directly followed by another digit token (e.g.\n// \"yyyyMM\") lets the year's own greediness silently eat digits meant for\n// the next token — same class of bug as UNPADDED_NUMERIC_TOKENS below, but\n// unbounded-width, so it can't reuse enumerateValidSplits' fixed-range\n// splitting. Restricting to exactly 4 digits whenever something could\n// follow closes that off entirely, at the cost of \"yyyyMM\" not being able\n// to represent a 5-digit year — an already-rare case doubly rare in\n// combination with a glued adjacent token.\nconst YYYY_EXACT = '-?\\\\d{4}';\nconst YYYY_EXTENDED = '-?\\\\d{4,}';\n\n// True for any token whose matched text can start with a digit — i.e.\n// every token here except the locale-named ones (MMMM/MMM/EEEE/EEE/a) and\n// zzz (which can start with a digit only via a fixed offset like \"+09:00\",\n// already handled by requiring a leading sign there). Used to decide\n// whether a \"yyyy\" immediately before this token needs the exact-4-digit\n// fragment instead of the open-ended one.\n// Exported for parsePattern.ts's ReDoS guard: a token whose regex\n// fragment can begin with a bare digit (see the guard comments there).\nexport const DIGIT_LEADING_TOKENS = new Set([\n 'yyyy', 'yy', 'MM', 'M', 'dd', 'd', 'HH', 'H', 'hh', 'h', 'mm', 'm', 'ss', 's',\n 'SSSSSSSSS', 'SSSSSSSS', 'SSSSSSS', 'SSSSSS', 'SSSSS', 'SSSS', 'SSS', 'SS', 'S',\n]);\n\nexport function tokenFragment(token: string, locale: string, nextToken?: string): string {\n if (token === 'yyyy') {\n return nextToken !== undefined && DIGIT_LEADING_TOKENS.has(nextToken) ? YYYY_EXACT : YYYY_EXTENDED;\n }\n\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n if (token === 'QQQ') {\n return QQQ_FRAGMENT;\n }\n\n if (FORMAT_ONLY_TOKENS.has(token)) {\n throw new UnknownTokenError({\n token,\n message:\n `temporal-fmt: token \"${token}\" is format-only — it can't be parsed back into a value. ` +\n `Use a different token in the parse format string (e.g. \"d\" for \"do\", \"MM\" for \"ww\").`,\n });\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-insensitive on purpose: \"pm\"/\"Pm\"/\"PM\" all mean the same thing,\n // and unlike the Md-glue ambiguity elsewhere in this file, there's no\n // second valid reading to guess wrong — so rejecting on case buys no\n // correctness, only friction against real-world data (mixed-case CSV\n // exports, lowercase log timestamps).\n case 'a': return alternation(vocab.dayPeriod, true);\n case 'zzz': return getTimeZoneFragment();\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n return OFFSET_SHAPES[token]!;\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. tokenFragment's only caller (buildCapturingPattern\n in parsePattern.ts) always passes piece.value straight from\n tokenize.ts, which only ever emits strings from tokens.ts's TOKENS\n table. Every token in that table is handled above: the numeric\n ones via NUMERIC_FRAGMENTS, QQQ via QQQ_FRAGMENT, the format-only\n ones via the FORMAT_ONLY_TOKENS check earlier in this function, and\n everything else via one of the switch cases. There's no token\n string that can reach this default without either tokens.ts\n registering something new or tokenize.ts being bypassed, neither of\n which happens on the parse() path. */\n default:\n throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token \"${token}\"` });\n /* c8 ignore stop @preserve */\n }\n}\n\n// Tokens whose fragment is variable-width (1-2 digits, no leading zero).\n// Two or more of these glued with no literal separator between them can\n// have more than one digit-split that's independently valid against every\n// fragment in the run — see the big comment on NUMERIC_FRAGMENTS above.\n// Reordering alternation branches picks a winner for *some* of these\n// cases, but can't make both directions of a pair (e.g. \"Md\" and \"dM\")\n// agree, because the ambiguity is in the input string itself, not in how\n// any one fragment is written. exported so parsePattern.ts can find runs\n// of these that need split-counting at match time instead of a single\n// fixed regex.\nexport const UNPADDED_NUMERIC_TOKENS = new Set(['M', 'd', 'H', 'h', 'm', 's']);\n\n// Every accept width for a given unpadded numeric token, as plain min/max\n// value + digit-length pairs — used to enumerate candidate splits of a\n// digit run at match time. Mirrors NUMERIC_FRAGMENTS's semantics exactly\n// (same accepted values), just as data instead of regex source, since\n// enumerating splits against a compiled regex per-candidate would be\n// slower and harder to reason about than checking numeric ranges directly.\nexport const UNPADDED_NUMERIC_RANGES: Record<string, Array<{ digits: 1 | 2; min: number; max: number }>> = {\n M: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n d: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 31 }],\n H: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 23 }],\n h: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n m: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n s: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n};\n\n/**\n * Given the literal digit string a run of N adjacent unpadded-numeric\n * tokens matched as a whole (e.g. \"112\" for a 2-token run), enumerates\n * every way to split it into N pieces (one per token, each piece 1-2\n * digits per that token's own width rule) and returns every split where\n * every piece is independently valid for its token. Length 0 means the\n * run's regex match shouldn't have been possible in the first place\n * (shouldn't happen — the caller only invokes this after the whole\n * pattern already matched, meaning at least one split exists: the one the\n * regex actually took). Length 1 means the reading is unambiguous.\n * Length 2+ means true ambiguity — the caller should throw rather than\n * pick one.\n *\n * Recursive over token count rather than hardcoded to 2, so a 3+ token\n * unseparated run (e.g. \"Hms\") is covered by the same logic without a\n * special case — those are rarer in practice but not impossible, and a\n * partial fix that only covered pairs would leave the identical bug for\n * anyone writing a 3-token glued run.\n */\nexport function enumerateValidSplits(digits: string, tokens: string[]): number[][] {\n const memo = new Map<string, number[][]>();\n\n function solve(tokenIndex: number, offset: number): number[][] {\n const key = `${tokenIndex}:${offset}`;\n const cached = memo.get(key);\n if (cached) {\n return cached;\n }\n\n if (tokenIndex === tokens.length) {\n const result = offset === digits.length ? [[]] : [];\n memo.set(key, result);\n return result;\n }\n\n const token = tokens[tokenIndex];\n const ranges = UNPADDED_NUMERIC_RANGES[token!];\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. enumerateValidSplits's only caller (parse.ts, both\n call sites) passes run.tokens straight from\n pattern.ambiguousRuns, which parsePattern.ts only ever populates\n with tokens already checked against UNPADDED_NUMERIC_TOKENS — the\n exact same key set as UNPADDED_NUMERIC_RANGES. There's no path\n where a token reaches here without having already passed that\n check. */\n if (!ranges) {\n throw new Error(`temporal-fmt: internal error — \"${token}\" is not an unpadded numeric token`);\n }\n /* c8 ignore stop @preserve */\n\n const results: number[][] = [];\n for (const { digits: width, min, max } of ranges) {\n if (offset + width > digits.length) continue;\n const piece = digits.slice(offset, offset + width);\n if (width === 2 && piece[0] === '0') continue;\n const value = Number(piece);\n if (value < min || value > max) continue;\n\n for (const restSplit of solve(tokenIndex + 1, offset + width)) {\n results.push([value, ...restSplit]);\n if (results.length === 2) break;\n }\n if (results.length === 2) break;\n }\n\n memo.set(key, results);\n return results;\n }\n\n return solve(0, 0);\n}","import type { Piece } from './tokenize.js';\nimport { tokenFragment, UNPADDED_NUMERIC_TOKENS, DIGIT_LEADING_TOKENS } from './pattern.js';\nimport { FormatSyntaxError } from './errors.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// Guards against catastrophic-backtracking (ReDoS) patterns.\n//\n// Every confirmed ReDoS in this library shares one shape: two or more\n// variable-width digit-consuming regex fragments placed so the engine\n// can't tell where one ends and the next begins — either glued with no\n// separator (\"MdMdMd…\", \"HmsHms…\") or separated only by a literal that\n// itself starts with a digit (\"M1M1M1…\", \"yyyy1yyyy1…\"). Each fragment\n// then has multiple ways to divide the digit run, and a failing match\n// makes the engine explore every combination — exponential in the number\n// of fragments. Measured against the pre-fix code: \"Md\"×13 (26-char\n// format, 40-char input) ≈ 2.7 s; \"yyyy1\"×8 (48-char format) ≈ 26 s;\n// both grow roughly ×3–14 per additional fragment.\n//\n// Three structural defenses, applied at pattern-build time:\n//\n// 1. A run of 2+ glued unpadded-numeric tokens is emitted as ONE\n// bounded digit group `(?<rN>\\d{R,2R})` instead of R separate\n// variable-width fragments. The per-token split is resolved after\n// the match by enumerateValidSplits() (pattern.ts) — the exact\n// machinery parse() already used to detect ambiguous glued runs —\n// so the documented behavior (unique split resolves; 2+ valid\n// splits throws in strict mode / heuristic-picks in lenient; 0\n// splits is a mismatch) is preserved. A lone `\\d{R,2R}` group\n// backtracks at most R+1 times, which is linear.\n//\n// 2. yyyy uses the exact `-?\\d{4}` fragment not only when the next\n// *token* is digit-leading (existing rule) but also when the next\n// *literal* starts with a digit. An open-ended `-?\\d{4,}` year\n// glued to a digit literal is the cheapest exponential engine\n// there is (unbounded width choices per year), and the exact form\n// matches everything the open-ended form matched in that position\n// except years with 5+ digits glued directly to an unquoted digit\n// literal — a pathological corner deliberately traded away.\n//\n// 3. An ambiguity budget for what's left: every adjacency between a\n// variable-width digit consumer (a lone unpadded token, or a glued\n// run group) and a digit-consuming successor (a digit-leading token\n// or a literal starting with a digit) costs log2 of the consumer's\n// width choices. A pattern whose total exceeds MAX_AMBIGUITY_BITS\n// (12 — a hard ceiling of 4096 backtrack paths) is rejected at\n// build time with a FormatSyntaxError. Realistic format strings\n// score 0–3; the \"M1M1M1…\" attack scores one bit per glued pair.\nconst MAX_AMBIGUITY_BITS = 12;\n\nfunction widthChoicesBits(choices: number): number {\n return Math.ceil(Math.log2(Math.max(choices, 1)));\n}\n\n// Does this piece's regex fragment START by consuming a bare digit?\n// (Tokens whose match can begin with 0-9, and literals whose first\n// character is a digit.) Used to find the boundaries where a preceding\n// variable-width digit consumer could trade digits with a successor.\nfunction isDigitConsumingStart(piece: Piece | undefined): boolean {\n if (piece === undefined) return false;\n if (piece.kind === 'literal') return /^[0-9]/.test(piece.value);\n return DIGIT_LEADING_TOKENS.has(piece.value);\n}\n\nexport interface CapturingPattern {\n regex: RegExp;\n groups: Array<{ name: string; token: string }>; // token pieces, in order\n // Runs of 2+ adjacent unpadded-numeric tokens with no literal separator\n // between them (e.g. \"Md\", \"Hms\"). Each run is captured by ONE regex\n // group named `groupName` spanning the run's whole digit run\n // (R..2·R digits); per-token values come from enumerateValidSplits()\n // at match time. `groupNames` lists the per-token group names — they\n // appear in `groups` for structure/positions but have no counterpart\n // in the regex itself, so consumers must read their values from the\n // split enumeration, not from match.groups.\n ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }>;\n}\n\n/**\n * Same walk as buildPatternSource() in pattern.ts, but each token piece\n * gets its own named capture group (positionally named so the same token,\n * e.g. \"yyyy\", could in theory appear twice) so a caller can pull the\n * matched substring for each token back out after a successful match.\n */\nexport function buildCapturingPattern(pieces: Piece[], locale: string): CapturingPattern {\n const groups: Array<{ name: string; token: string }> = [];\n const ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }> = [];\n let source = '';\n let i = 0;\n let ambiguityBits = 0;\n\n // Tracks the current run of adjacent unpadded-numeric token pieces (no\n // literal or non-unpadded token has broken it yet). Names are recorded\n // in declaration order so a run of 2+ can emit one group while its\n // member tokens still get individual entries in `groups`.\n let currentRun: { names: string[]; tokens: string[] } = { names: [], tokens: [] };\n\n const flushRun = (nextPiece: Piece | undefined) => {\n if (currentRun.tokens.length === 1) {\n // A lone unpadded token: emit its normal (two-way) fragment. Two\n // width choices are harmless on their own; if the next element\n // can consume a digit, charge one ambiguity bit for the boundary.\n const name = currentRun.names[0]!;\n const token = currentRun.tokens[0]!;\n source += `(?<${name}>${tokenFragment(token, locale)})`;\n if (isDigitConsumingStart(nextPiece)) ambiguityBits += 1;\n } else if (currentRun.tokens.length >= 2) {\n // Emit the accumulated run as ONE bounded digit group. R unpadded\n // tokens accept between R and 2R digits in total; anything outside\n // that span can't match regardless of how the digits split, so the\n // single group's acceptance region is exactly the union of the old\n // per-token fragments' regions. Backtracking into this group is\n // bounded at R+1 width choices — linear, not exponential.\n const runName = `r${i++}`;\n const tokenCount = currentRun.tokens.length;\n source += `(?<${runName}>\\\\d{${tokenCount},${tokenCount * 2}})`;\n // Note: no `groups` entry for the run group itself — `groups` lists\n // per-token pieces only (its members were already pushed when\n // visited), so consumers like parseToParts see exactly one entry\n // per token, same as before this run-group change. The regex group\n // is reached through ambiguousRuns[].groupName.\n ambiguousRuns.push({\n groupName: runName,\n groupNames: currentRun.names,\n tokens: currentRun.tokens,\n });\n // A run group adjacent to a digit-consuming successor keeps its\n // (R+1) width choices at that boundary — charge the budget.\n if (isDigitConsumingStart(nextPiece)) {\n ambiguityBits += widthChoicesBits(tokenCount + 1);\n }\n }\n currentRun = { names: [], tokens: [] };\n };\n\n for (const [idx, piece] of pieces.entries()) {\n if (piece.kind === 'literal') {\n flushRun(piece);\n source += escapeRegExp(piece.value);\n continue;\n }\n\n if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n // Part of a (potential) glued run — defer fragment emission to\n // flushRun so a run of 2+ collapses into one bounded group.\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n currentRun.names.push(name);\n currentRun.tokens.push(piece.value);\n continue;\n }\n\n flushRun(piece);\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n const nextPiece = pieces[idx + 1];\n const nextToken = nextPiece?.kind === 'token' ? nextPiece.value : undefined;\n // yyyy picks its fragment based on what follows: the exact 4-digit\n // form whenever a digit-consuming element comes next (digit-leading\n // token — the pre-existing rule — OR a literal starting with a\n // digit, added by the ReDoS fix; see the guard block above). The\n // open-ended form is only safe when nothing digit-consuming can\n // follow it.\n if (piece.value === 'yyyy' && nextToken === undefined && isDigitConsumingStart(nextPiece)) {\n source += `(?<${name}>${tokenFragment(piece.value, locale, 'M')})`;\n } else {\n source += `(?<${name}>${tokenFragment(piece.value, locale, nextToken)})`;\n }\n }\n flushRun(undefined);\n\n if (ambiguityBits > MAX_AMBIGUITY_BITS) {\n throw new FormatSyntaxError({\n reason:\n `format string has too many variable-width numeric tokens glued to digit-consuming neighbors ` +\n `(ambiguity score ${ambiguityBits} > ${MAX_AMBIGUITY_BITS}). ` +\n `This shape makes the regex engine backtrack exponentially on near-miss input. ` +\n `Add a non-digit separator between these tokens (e.g. \"-\" or \" \") or use their padded forms (MM/dd/HH/mm/ss).`,\n });\n }\n\n // 'd' flag enables match.indices.groups — used by parseToParts to\n // report each token's actual position in the input. Without it,\n // computing per-group positions would require a separate walk of the\n // piece list against the input, duplicating logic the regex already\n // has. Backward-compatible: 'd' only adds an `indices` property to\n // the match result, no behavioral change to the match itself.\n return { regex: new RegExp(`^(?:${source})$`, 'ud'), groups, ambiguousRuns };\n}\n","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildCapturingPattern, type CapturingPattern } from './parsePattern.js';\nimport { enumerateValidSplits, isValidTimeZone } from './pattern.js';\nimport { getLocaleVocab, canonicalCacheKey, assertValidLocaleTag, subscribeToVocabChanges } from './localeVocab.js';\nimport { getTemporal } from './temporalProvider.js';\nimport { MAX_FORMAT_LENGTH, MAX_INPUT_LENGTH } from './constants.js';\nimport { TemporalFmtError, InvalidTimeZoneError, InvalidOffsetError, FormatSyntaxError, ParseMismatchError, AmbiguousInputError, InvalidDateError, wrapUntypedError } from './errors.js';\nimport { applyParseNumbering, type NumberingParseOptions } from './numbering.js';\n\n// format strings are short hand-written literals reused across many calls —\n// cache the compiled capturing pattern per (formatStr, locale) pair instead\n// of rebuilding it every call.\nconst patternCache = new Map<string, CapturingPattern>();\nconst MAX_CACHE_SIZE = 500;\n\n// A compiled pattern embeds locale-vocabulary alternations (MMMM/MMM/\n// EEEE/EEE/a fragments) at build time. When registerLocaleVocab()\n// swaps a locale's vocabulary, every cached pattern for any locale\n// becomes potentially stale — the cached regex would keep matching the\n// OLD vocabulary while format() renders the new one, breaking the\n// format→parse round-trip for the library's own output. Clear the whole\n// cache on any vocab change: rebuilds are cheap and self-limiting via\n// the cache size cap.\nsubscribeToVocabChanges(() => { patternCache.clear(); });\n\nfunction getPattern(formatStr: string, locale: string): CapturingPattern {\n const key = JSON.stringify([canonicalCacheKey(locale), 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 pattern = buildCapturingPattern(tokenize(formatStr), locale);\n patternCache.set(key, pattern);\n return pattern;\n}\n\n// Requires an explicit `-u-ca-` extension (e.g. 'en-u-ca-hebrew') to apply\n// a non-Gregorian calendar, per parse()'s own docstring. 'gregory' counts\n// as \"no calendar\" so the default locale keeps constructing plain ISO 8601.\n//\n// Used to key off resolvedOptions().calendar instead — a locale's\n// *default* calendar, whether the caller asked for one or not. That broke\n// th-TH silently: its default is 'buddhist', so plain Gregorian-looking\n// digits parsed 543 years off, while format() has no matching calendar\n// step and just prints the object's own ISO fields either way.\nconst calendarCache = new Map<string, string | undefined>();\nconst MAX_CALENDAR_CACHE_SIZE = 500;\n\nfunction resolveCalendar(locale: string): string | undefined {\n // Typed validation first: a genuinely malformed tag (bare private-use\n // singleton, control characters, garbage) surfaces as the library's\n // InvalidLocaleError instead of a raw engine RangeError — same code as\n // before, different (documented, structured) error class.\n assertValidLocaleTag(locale);\n // canonicalCacheKey is memoized (localeVocab.ts), so the common path —\n // repeated parse() calls with the same locale — no longer constructs a\n // fresh Intl.Locale per call just to compute the calendar-cache key.\n const canonicalLocale = canonicalCacheKey(locale);\n if (calendarCache.has(canonicalLocale)) {\n return calendarCache.get(canonicalLocale);\n }\n if (calendarCache.size >= MAX_CALENDAR_CACHE_SIZE) {\n const oldestKey = calendarCache.keys().next().value;\n if (oldestKey !== undefined) calendarCache.delete(oldestKey);\n }\n let calendar: string | undefined;\n const parts = canonicalLocale.split('-');\n const extensionIndex = parts.indexOf('u');\n const calendarKeyIndex = extensionIndex === -1 ? -1 : parts.indexOf('ca', extensionIndex + 1);\n if (calendarKeyIndex !== -1 && calendarKeyIndex + 1 < parts.length) {\n const resolved = new Intl.DateTimeFormat(canonicalLocale).resolvedOptions().calendar;\n calendar = resolved === 'gregory' ? undefined : resolved;\n }\n calendarCache.set(canonicalLocale, calendar);\n return calendar;\n}\n\ninterface Fields {\n year?: number;\n twoDigitYear?: number;\n month?: number;\n day?: number;\n hour?: number;\n hour12?: number;\n dayPeriodRaw?: string;\n isPM?: boolean;\n minute?: number;\n second?: number;\n millisecond?: number;\n microsecond?: number;\n nanosecond?: number;\n timeZoneId?: string;\n // Canonical `+HH:MM` form of any offset token (X/XX/XXX/x/xx/xxx)\n // captured in this pattern. Distinct from timeZoneId because the two\n // can coexist in the same pattern (e.g. \"yyyy-MM-dd HH:mm zzz XXX\") —\n // see the cross-check after construction for how a mismatch between\n // them is resolved.\n offsetString?: string;\n weekdayExpected?: number;\n weekdayRaw?: string;\n quarter?: number;\n}\n\n// Normalizes a captured offset-token string into the canonical `+HH:MM`\n// shape Temporal.ZonedDateTime.from accepts as a `timeZone` value. Throws\n// descriptive errors for out-of-range hours/minutes, since the regex\n// shape (OFFSET_SHAPES in pattern.ts) is deliberately permissive — a\n// post-match range check here gives the user a specific error (\"offset\n// hours 99 out of range, max 14\") instead of \"no valid pattern matches\".\n//\n// Range bounds: -12:00 to +14:00, the standard IANA offset range\n// (Baker/Howland at -12, Kiritimati at +14). +14:01 / -12:01 etc. are\n// rejected explicitly even though the per-piece bounds (hours ≤ 14,\n// minutes ≤ 59) alone wouldn't catch them.\nfunction parseOffsetString(raw: string, token: string): string {\n if (raw === 'Z') {\n /* c8 ignore start @preserve -- unreachable: lowercase tokens' regex\n (OFFSET_SHAPES in pattern.ts) has no \"Z\" alternative at all, so\n raw === 'Z' can only ever be reached when token is one of the\n uppercase variants (X/XX/XXX). A lowercase token can't even\n capture \"Z\" as `raw` in the first place. */\n if (token === 'x' || token === 'xx' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" doesn't accept \"Z\" — only the uppercase variants (X/XX/XXX) emit \"Z\" for UTC. ` +\n `Use \"+00:00\", \"+0000\", or \"+00\" depending on the variant's width.`\n );\n }\n /* c8 ignore stop @preserve */\n return '+00:00';\n }\n\n const sign = raw[0];\n /* c8 ignore start @preserve -- unreachable: raw is a regex-captured\n group from an offset token, and every OFFSET_SHAPES pattern\n (pattern.ts) is anchored to either \"Z\" or a leading [+-]. raw's\n first character can never be anything else by the time it reaches\n this function. */\n if (sign !== '+' && sign !== '-') {\n throw new Error(`temporal-fmt: offset \"${raw}\" for token \"${token}\" doesn't start with \"+\", \"-\", or \"Z\".`);\n }\n /* c8 ignore stop @preserve */\n const body = raw.slice(1);\n let hoursStr: string;\n let minutesStr: string;\n if (body.length === 2) {\n /* c8 ignore start @preserve -- unreachable: each offset token's own\n regex shape in OFFSET_SHAPES (pattern.ts) already gates which\n body shapes it can capture. Only X and x ever match a 2-digit\n body — XX/xx/XXX/xxx's regexes can't produce one — so this\n mismatch can never actually fire through parse(). */\n // +HH — only X/x emit this shape; XX/xx/XXX/xxx always carry minutes.\n if (token !== 'X' && token !== 'x') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires minutes, but \"${raw}\" has none.`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body;\n minutesStr = '00';\n } else if (body.length === 4) {\n /* c8 ignore start @preserve -- unreachable, same reason as the\n 2-digit case above: XXX/xxx's regex requires a colon, so it can\n never capture a 4-digit no-colon body in the first place. */\n // +HHMM — X/x (when minutes are non-zero) or XX/xx.\n if (token === 'XXX' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires a colon between hours and minutes (e.g. \"${sign}${body.slice(0, 2)}:${body.slice(2)}\").`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(2, 4);\n } else if (body.length === 5 && body[2] === ':') {\n /* c8 ignore start @preserve -- unreachable, same reason again: only\n XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never\n capture one. */\n // +HH:MM — XXX/xxx only.\n if (token !== 'XXX' && token !== 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it doesn't use a colon (use \"${sign}${body.slice(0, 2)}${body.slice(3)}\" instead).`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(3, 5);\n /* c8 ignore start @preserve -- unreachable: every offset token's\n regex only ever produces a body of length 2, length 4, or length 5\n with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no\n shape falls outside those three cases, so this else arm can't be\n taken through parse(). Kept as an exhaustiveness fallback so\n hoursStr/minutesStr are assigned on every path TypeScript can see. */\n } else {\n throw new Error(`temporal-fmt: offset \"${raw}\" doesn't match the shape token \"${token}\" accepts.`);\n }\n /* c8 ignore stop @preserve */\n\n const hours = Number(hoursStr);\n const minutes = Number(minutesStr);\n // Per-piece range checks catch most malformed input.\n if (hours > 14) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset hours ${hours} in \"${raw}\" out of range (max 14 — Kiritimati, Line Islands is +14:00).`,\n });\n }\n if (minutes > 59) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset minutes ${minutes} in \"${raw}\" out of range (max 59).`,\n });\n }\n // Boundary: +14:01..+14:59 and -12:01..-12:59 are out of range even\n // though each piece alone is in bounds — the overall offset exceeds\n // the IANA-supported range.\n if (sign === '+' && hours === 14 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported UTC offset of +14:00.`,\n });\n }\n if (sign === '-' && hours === 12 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported negative UTC offset of -12:00.`,\n });\n }\n return `${sign}${hoursStr}:${minutesStr}`;\n}\n\nfunction assignField<T>(fields: Fields, key: keyof Fields, value: T): void {\n (fields as Record<string, T | undefined>)[key] = value;\n}\n\nfunction applyGroup(fields: Fields, token: string, raw: string, locale: string, formatStr: string): void {\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'yyyy':\n assignField(fields, 'year', Number(raw));\n break;\n case 'yy':\n assignField(fields, 'twoDigitYear', Number(raw));\n break;\n case 'MM': case 'M':\n assignField(fields, 'month', Number(raw));\n break;\n case 'MMMM':\n assignField(fields, 'month', vocab.monthLong.indexOf(raw) + 1);\n break;\n case 'MMM':\n assignField(fields, 'month', vocab.monthShort.indexOf(raw) + 1);\n break;\n case 'dd': case 'd':\n assignField(fields, 'day', Number(raw));\n break;\n case 'EEEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayLong.indexOf(raw) + 1);\n break;\n case 'EEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayShort.indexOf(raw) + 1);\n break;\n case 'HH': case 'H':\n assignField(fields, 'hour', Number(raw));\n break;\n case 'hh': case 'h':\n assignField(fields, 'hour12', Number(raw));\n break;\n case 'mm': case 'm':\n assignField(fields, 'minute', Number(raw));\n break;\n case 'ss': case 's':\n assignField(fields, 'second', Number(raw));\n break;\n case 'S': case 'SS': case 'SSS': case 'SSSS': case 'SSSSS':\n case 'SSSSSS': case 'SSSSSSS': case 'SSSSSSSS': case 'SSSSSSSSS': {\n // The captured digits are the leading N digits of a nanosecond-of-second\n // value, not the whole thing — \"5\" under SSSSSSSSS means 500000000ns\n // (half a second), not 5ns. Right-padding to 9 digits before splitting\n // is what makes that work; left-padding (or just Number(raw)) would\n // read \"5\" as 5ns instead.\n const nanoOfSecond = Number(raw.padEnd(9, '0'));\n assignField(fields, 'millisecond', Math.floor(nanoOfSecond / 1_000_000));\n assignField(fields, 'microsecond', Math.floor(nanoOfSecond / 1_000) % 1_000);\n assignField(fields, 'nanosecond', nanoOfSecond % 1_000);\n break;\n }\n case 'a': {\n // Matches case-insensitively (see pattern.ts's foldCase), so the\n // lookup here has to fold too, or \"pm\" would pass the regex and\n // then fail this indexOf against the exact-case vocab.\n const periodIndex = vocab.dayPeriod.findIndex((p) => p.toLowerCase() === raw.toLowerCase());\n /* c8 ignore start @preserve -- unreachable: the 'a' token's regex\n fragment (pattern.ts's alternation() over vocab.dayPeriod) can\n only ever capture a case-insensitive match of one of\n vocab.dayPeriod's own entries. Both the regex and this lookup\n derive their vocab from the same `locale` via getLocaleVocab(),\n so periodIndex can't come back negative through parse(). */\n if (periodIndex < 0) throw new Error(`temporal-fmt: unknown day period \"${raw}\" for locale \"${locale}\".`);\n /* c8 ignore stop @preserve */\n assignField(fields, 'dayPeriodRaw', raw);\n assignField(fields, 'isPM', periodIndex === 1);\n break;\n }\n case 'zzz':\n assignField(fields, 'timeZoneId', raw);\n break;\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n assignField(fields, 'offsetString', parseOffsetString(raw, token));\n break;\n case 'Q':\n assignField(fields, 'quarter', Number(raw));\n break;\n case 'QQQ':\n // strips the literal \"Q\" prefix the token itself formats; the suffix\n // digit is the quarter value 1-4\n assignField(fields, 'quarter', Number(raw.slice(1)));\n break;\n }\n}\n\n// The lenient split-selection heuristic for ambiguous glued numeric runs.\n// See README \"Lenient parse mode\" — the strict default throws on these,\n// lenient mode opts into picking one split instead.\nfunction pickLenientSplit(splits: number[][], tokens: string[]): number[] {\n // Prefer the split where a \"d\" (day) token, if any, has a value of 12 or\n // less. Rationale: when a person writes a glued run like \"121\" for an\n // Md format string, the reading \"Dec 1\" (M=12, d=1) is what they\n // typically meant — if they meant \"Jan 21\" they would more often have\n // written it as \"1/21\" or \"01/21\" with a separator or padding, since the\n // 2-digit day is the more naturally-cohesive unit to keep glued. This\n // isn't a guarantee, which is exactly why lenient mode is opt-in — but\n // it's a reasonable default when the caller has asked us to guess.\n const dayIndex = tokens.indexOf('d');\n if (dayIndex !== -1) {\n const smallDaySplits = splits.filter((s) => s[dayIndex]! <= 12);\n if (smallDaySplits.length > 0) {\n return smallDaySplits[0]!;\n }\n }\n // Fallback to the first valid split when the day heuristic doesn't\n // narrow it down — deterministic, and \"first\" here means \"whichever\n // enumerateValidSplits returned first\", which is a depth-first\n // leftmost-shortest walk over the candidate splits.\n return splits[0]!;\n}\n\n// emulates strptime (POSIX) for 2-digit years so the result doesn't depend\n// on the current clock: 00-68 -> 2000-2068, 69-99 -> 1900-1999\n// https://www.man7.org/linux//man-pages/man3/strptime.3p.html\nfunction resolveYear(fields: Fields): number | undefined {\n if (fields.year !== undefined && fields.twoDigitYear !== undefined) {\n // FormatSyntaxError, not ParseMismatchError: this is a contradiction\n // in the format string itself (both \"yyyy\" and \"yy\" present), not a\n // mismatch between the format and a specific input.\n throw new FormatSyntaxError({\n message: 'temporal-fmt: format string mixes \"yyyy\" and \"yy\" year representations.',\n });\n }\n if (fields.year !== undefined) return fields.year;\n if (fields.twoDigitYear !== undefined) {\n return fields.twoDigitYear <= 68 ? 2000 + fields.twoDigitYear : 1900 + fields.twoDigitYear;\n }\n return undefined;\n}\n\nfunction resolveHour(fields: Fields, formatStr: string, locale: string): number | undefined {\n if (fields.hour !== undefined && fields.hour12 !== undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n `12-hour token (\"hh\"/\"h\").`,\n });\n }\n if (fields.hour !== undefined) {\n if (fields.dayPeriodRaw !== undefined) {\n const vocab = getLocaleVocab(locale);\n const expected = fields.hour < 12 ? vocab.dayPeriod[0] : vocab.dayPeriod[1];\n if (fields.dayPeriodRaw.toLowerCase() !== expected?.toLowerCase()) {\n throw new FormatSyntaxError({\n format: formatStr,\n message: `temporal-fmt: format string \"${formatStr}\" contains a day period that contradicts the 24-hour value.`,\n });\n }\n }\n return fields.hour;\n }\n if (fields.hour12 !== undefined) {\n if (fields.isPM === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" uses a 12-hour token (\"hh\"/\"h\") without an \"a\" token, ` +\n `so parse() can't tell AM from PM.`,\n });\n }\n return (fields.hour12 % 12) + (fields.isPM ? 12 : 0);\n }\n return undefined;\n}\n\n/**\n * Parses `input` against `formatStr` and builds the real Temporal value it\n * describes: a `Temporal.PlainDate`, `PlainTime`, `PlainDateTime`, or\n * `ZonedDateTime` depending on which tokens are present.\n *\n * Returns `unknown` — this package has no ambient `Temporal` types to return\n * a real one against.\n *\n * `options.locale` picks the calendar the result is built in. Pass a locale\n * tag with a `-u-ca-` extension (e.g. `'en-u-ca-hebrew'`) to parse into a\n * non-Gregorian calendar.\n *\n * @throws if `input` doesn't match `formatStr`'s shape at all\n * @throws if it matches the shape but describes an impossible date (e.g. Feb\n * 30) or self-contradictory data (e.g. a weekday name that doesn't match the\n * actual date)\n *\n * @example\n * parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // Temporal.PlainDateTime\n * parse('yyyy-MM', '2026-08-04T15:45:30') // throws — shape doesn't match\n * parse('yyyy-MM-dd', '2026-02-30') // throws — not a real date\n */\nexport function parse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Transliterate non-ASCII numerals to ASCII before any matching happens,\n // when the caller opts in via parseNumberingSystem. Every regex this\n // module builds expects 0-9; this is the one place that assumption\n // could otherwise be violated by locale-native input digits.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const calendar = resolveCalendar(locale);\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n\n // The regex's zzz fragment only matches a bounded zone-id *shape* (see\n // TIME_ZONE_SHAPE in pattern.ts) rather than alternating every real IANA\n // name inline, so a shape match isn't proof of a real zone yet — check\n // Zone ids can't be enumerated in the regex itself (there are ~400 of\n // them and they change over time as IANA updates the tz database), so\n // the regex only captures the zzz group's shape and this loop checks\n // each captured zzz group against the actual zone list here. Unlike a\n // regex-shape mismatch, this failure has a specific cause worth\n // naming: the shape matched but the zone id itself isn't recognized.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // A run of 2+ adjacent unpadded-numeric tokens with no literal separator\n // (e.g. \"Md\", \"dM\", \"Hms\") is captured by a single bounded digit group in\n // the regex (see buildCapturingPattern — per-token variable-width\n // fragments made near-miss matching exponential in the number of glued\n // tokens). Resolve each run's per-token split here instead: unique valid\n // split resolves (identical to what the old regex's own greedy match\n // produced, since the old match was always one of the valid splits);\n // 2+ valid splits is genuinely ambiguous input — strict mode throws,\n // lenient opts into the documented heuristic. A span with 0 valid\n // splits matched the run's width window but names no valid per-token\n // assignment, which the old per-token fragments rejected at match\n // time — surface the same \"no valid pattern matches\" error.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n // Strict default — throw on ambiguity. The whole point of the\n // library's parse() is to refuse to guess when the same input has\n // more than one valid reading. Lenient mode (opt-in via\n // options.lenient) instead picks one split via a documented\n // heuristic — see pickLenientSplit() above and the README section\n // \"Lenient parse mode\" for why this is strictly additive and never\n // the default.\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n const fields: Fields = {};\n // Per-token values for glued-run members come from the split\n // enumeration above (runValues); every other token reads its own\n // regex group directly.\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n applyGroup(fields, token, raw, locale, formatStr);\n }\n\n const year = resolveYear(fields);\n const hour = resolveHour(fields, formatStr, locale);\n const { month, day, minute, second, millisecond, microsecond, nanosecond, timeZoneId, offsetString, weekdayExpected, weekdayRaw, quarter } = fields;\n\n const hasAnyDatePart = year !== undefined || month !== undefined || day !== undefined;\n const hasFullDate = year !== undefined && month !== undefined && day !== undefined;\n if (hasAnyDatePart && !hasFullDate) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an incomplete date — ` +\n `year, month, and day tokens must all be present together.`,\n });\n }\n\n const hasTime = hour !== undefined || minute !== undefined || second !== undefined || millisecond !== undefined;\n\n if (timeZoneId !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a \"zzz\" token but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n // Mirror zzz's full-date-and-time requirement: an offset alone is\n // meaningless without a wall-clock instant to anchor it to. Throws the\n // same kind of \"needs full date and time\" error zzz throws — separate\n // message so a caller reading it can tell which token type they\n // forgot to pair with a full date+time.\n if (offsetString !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n if (weekdayExpected !== undefined && !hasFullDate) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a weekday token (\"EEEE\"/\"EEE\") but needs ` +\n `a full date to validate it against.`,\n });\n }\n\n if (!hasFullDate && !hasTime) {\n // shouldn't happen — every token maps to a date, time, zone, or\n // weekday field, and weekday-without-date already threw above\n throw new FormatSyntaxError({\n format: formatStr,\n message: `temporal-fmt: format string \"${formatStr}\" has no date or time tokens to parse.`,\n });\n }\n\n const temporal = getTemporal();\n const timeFields = {\n hour: hour ?? 0,\n minute: minute ?? 0,\n second: second ?? 0,\n millisecond: millisecond ?? 0,\n microsecond: microsecond ?? 0,\n nanosecond: nanosecond ?? 0,\n };\n // omitted entirely for the default calendar (see resolveCalendar) so\n // construction stays plain ISO 8601 unless a caller's locale asks for\n // something else — Temporal calendars don't apply to time-only values.\n const calendarField = calendar ? { calendar } : {};\n\n // overflow: 'reject' — without it Temporal *clamps* out-of-range fields\n // (Feb 30 silently becomes Feb 28) instead of throwing, which would\n // contradict the \"throws on genuinely invalid data\" behavior parse() promises.\n const reject = { overflow: 'reject' as const };\n\n let result: unknown;\n try {\n if (timeZoneId !== undefined) {\n // offset: 'prefer' never throws on a mismatch — it just falls back to\n // the zone's real offset at this instant, silently overriding\n // whatever the offset token said. That's also what resolves a\n // repeated wall-clock time (DST fall-back): without an explicit\n // offset, Temporal defaults to the first occurrence, so passing the\n // token's offset here is what lets a second-occurrence input resolve\n // to the second occurrence instead of always falling back to the\n // first. Either way, \"prefer\" can't be used to detect disagreement —\n // that's checked explicitly below, once we have a real ZonedDateTime\n // to compare against, instead of relying on the wording of whatever\n // error Temporal's active implementation happens to throw (that\n // wording isn't part of the spec and differs between the native\n // Temporal global and userland polyfills).\n const zoneOptions: Temporal.ZonedDateTimeFromOptions = { overflow: 'reject', offset: 'prefer' };\n result = temporal.ZonedDateTime.from(\n {\n year: year!, month: month!, day: day!, ...timeFields, ...calendarField,\n timeZone: timeZoneId,\n ...(offsetString !== undefined ? { offset: offsetString } : {}),\n },\n zoneOptions\n );\n if (offsetString !== undefined) {\n // 'prefer' silently rewrites the wall-clock time itself when the\n // input falls in a DST gap (the time never occurred, so there's\n // no instant to prefer toward) — it doesn't just pick a\n // different offset for the same clock time, the way it does for\n // an overlap. Checking offsetString alone can't tell \"gap,\n // silently moved\" apart from \"overlap, correctly resolved,\"\n // since both can produce an actualOffset that differs from what\n // was parsed. Comparing the wall-clock fields catches the gap\n // case: they can only drift from the parsed input if Temporal\n // moved the clock time to escape the gap.\n //\n // Only checked when an offset token was given: with no offset\n // token to disagree with, a gap shifting forward is the\n // documented, wanted behavior (there's nothing to reject against).\n const zdt = result as Temporal.ZonedDateTime;\n const wallClockShifted =\n zdt.hour !== timeFields.hour ||\n zdt.minute !== timeFields.minute ||\n zdt.second !== timeFields.second;\n if (wallClockShifted) {\n // InvalidDateError, not AmbiguousInputError: the message\n // *mentions* \"not an ambiguous... instant\" as a negation, which\n // is a false-positive match against wrapUntypedError's\n // /ambiguous/i classifier regex — the actual failure here is a\n // wall-clock time that doesn't exist (DST gap), which is an\n // invalid-value problem, not an ambiguous-reading-of-input\n // problem the way the numeric-token-glue ambiguity above is.\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `\"${timeZoneId}\" has no such wall-clock time on this date — it falls in a DST gap, ` +\n `not an ambiguous or valid instant.`,\n });\n }\n const actualOffset = zdt.offset;\n if (actualOffset !== offsetString) {\n throw new ParseMismatchError({\n input, format: formatStr,\n message:\n `has both a \"zzz\" zone (${timeZoneId}) and an offset token (${offsetString}), ` +\n `but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`,\n });\n }\n }\n } else if (offsetString !== undefined) {\n // Pattern had an offset token but no zzz. Use the offset string\n // directly as the timeZone — Temporal accepts a fixed-offset\n // string and produces a ZonedDateTime whose timeZoneId is the\n // offset string itself (e.g. \"+09:00\"). Same shape zzz produces\n // when it parses a fixed offset, just reached via a different\n // token.\n result = temporal.ZonedDateTime.from({ year: year!, month: month!, day: day!, ...timeFields, ...calendarField, timeZone: offsetString }, reject);\n } else if (hasFullDate && hasTime) {\n result = temporal.PlainDateTime.from({ year: year!, month: month!, day: day!, ...timeFields, ...calendarField }, reject);\n } else if (hasFullDate) {\n result = temporal.PlainDate.from({ year: year!, month: month!, day: day!, ...calendarField }, reject);\n } else {\n result = temporal.PlainTime.from(timeFields, reject);\n }\n } catch (err) {\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${input}\" doesn't describe a valid date/time for format \"${formatStr}\": ` +\n `${(err as Error).message}`,\n });\n }\n\n if (weekdayExpected !== undefined) {\n const actual = (result as { dayOfWeek: number }).dayOfWeek;\n if (actual !== weekdayExpected) {\n const vocab = getLocaleVocab(locale);\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${weekdayRaw}\" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) ` +\n `for the parsed date.`,\n });\n }\n }\n\n // Q/QQQ is a derived field of the month: 1-3 -> Q1, 4-6 -> Q2, 7-9 -> Q3,\n // 10-12 -> Q4. If a format string carries a quarter token alongside\n // month/date tokens, parse() cross-checks the parsed quarter against the\n // month the same way EEEE cross-checks weekday against date — silently\n // accepting a mismatch would defeat the point of having a quarter token\n // at all, since you'd be telling parse() one thing and the date another.\n if (quarter !== undefined && month !== undefined) {\n const expectedQuarter = Math.ceil(month / 3);\n if (quarter !== expectedQuarter) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" contains a quarter token (Q/QQQ) whose value ` +\n `(Q${quarter}) disagrees with the parsed month's actual quarter — month ${month} is in ` +\n `Q${expectedQuarter}.`,\n });\n }\n }\n\n return result;\n}\n\n// safeParse: returns a discriminated union instead of throwing. The\n// happy path returns `{ ok: true, value }` with the Temporal instance\n// (typed as `unknown` since this package has no ambient Temporal types).\n// The error path returns `{ ok: false, error }` where `error` is a\n// `TemporalFmtError` subclass when the failure is one the typed-error\n// surface in errors.ts knows how to classify (most of them), or a\n// wrapped plain `Error` (still inside a TemporalFmtError shell) when\n// the throw site hasn't been migrated yet. Callers needing the original\n// thrown object for backward compatibility should use parse() directly.\nexport type SafeParseResult =\n | { ok: true; value: unknown }\n | { ok: false; error: TemporalFmtError };\n\nexport function safeParse(formatStr: string, input: string, options: NumberingParseOptions = {}): SafeParseResult {\n try {\n return { ok: true, value: parse(formatStr, input, options) };\n } catch (err) {\n // Pass through typed errors unchanged — preserves the structured\n // fields (code/token/position/etc.) the existing typed-error\n // surface already populated.\n //\n // Every reachable throw site in parse()'s call graph now throws a\n // TemporalFmtError directly, so this condition is always true in the\n // current test suite; the false path (falling through to\n // wrapUntypedError below) is kept as a safety net for any throw site\n // added later without being migrated to a typed class immediately —\n // removing it would silently break the \"safeParse always returns a\n // TemporalFmtError\" contract documented above. Ignoring the whole\n // if/else as one unit is harmless for the true branch (real tests\n // still execute and count it — c8 just excludes it from the\n // denominator), and it's the only shape that actually suppresses the\n // false-branch marker, since c8 has no standalone \"ignore else\".\n /* c8 ignore start @preserve */\n if (err instanceof TemporalFmtError) {\n return { ok: false, error: err };\n }\n return { ok: false, error: wrapUntypedError(err as Error, { input, format: formatStr }) };\n }\n /* c8 ignore stop @preserve */\n}\n\n// tryParse: best-effort variant. Returns the parsed value or undefined.\n// Suppresses diagnostics entirely — when callers need the reason for\n// a failure, they should use safeParse(). Intentionally loose on the\n// return type (unknown) since this package has no ambient Temporal\n// types to return a real one against.\nexport function tryParse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n try {\n return parse(formatStr, input, options);\n } catch {\n return undefined;\n }\n}\n\n// parseToParts: returns the matched groups with token labels, before\n// any Temporal construction. Useful for callers that want to inspect\n// what each token captured (e.g. to build a non-Temporal result, or to\n// cross-check fields themselves) without committing to the inferred\n// Temporal type parse() would build.\n//\n// Throws the same errors parse() throws for early validation (unknown\n// token, unterminated quote, no-match, ambiguity in strict mode) since\n// those failures happen before any group assignment. Construction-time\n// errors (Feb 30, weekday mismatch, etc.) do not happen here —\n// parseToParts doesn't construct anything, so it can't fail at that step.\nexport interface ParsedPart {\n token: string;\n raw: string;\n // Field name (year/month/day/...) this token would assign if handed\n // to parse()'s applyGroup loop. undefined for tokens that don't map\n // to a single field (none today, but kept here so future additions\n // don't have to widen the type).\n field?: string;\n // Position of `raw` in `input`, 0-indexed. Lets a caller highlight\n // the matched span in an editor/CLI.\n position: number;\n}\n\nexport function parseToParts(formatStr: string, input: string, options: NumberingParseOptions = {}): ParsedPart[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Same numeral transliteration parse() does — see the comment there.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n // Same zzz shape-validation parse() does — kept here for parity, so\n // a caller using parseToParts sees the same InvalidTimeZoneError for\n // a bogus zone id, not a silently-accepted bogus zone.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // Glued-run split handling: same as parse() — each run's single regex\n // group is split into per-token values by enumerateValidSplits();\n // unique split resolves, 2+ splits throws in strict mode / heuristic-\n // picks in lenient, 0 splits is a shape mismatch. parseToParts mirrors\n // parse() so callers switching between the two on the same input get\n // consistent results.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n\n const parts: ParsedPart[] = [];\n // match.indices.groups (provided by the regex 'd' flag) gives the\n // [start, end] of each named group in the input. Used here so positions\n // are accurate even when literals separate tokens — summing raw\n // lengths alone wouldn't account for the literal characters between\n // groups. Falls back to the cumulative-raw-length heuristic on engines\n // without 'd' support (none we target, but the fallback keeps the\n // code robust if the flag is ever removed).\n const indices = (match as RegExpMatchArray & { indices?: { groups?: Record<string, [number, number]> } }).indices;\n const groupIndices = indices?.groups;\n let consumed = 0;\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n // Glued-run members have no regex group of their own — the regex's\n // recorded indices point at the run's overall span, not the\n // individual token's slice within it — so fall back to cumulative-\n // raw-length for them. Positions stay monotonic but may not be\n // exact for tokens inside a resolved run. Documented as a known\n // limitation; the alternative (re-running the regex with the chosen\n // split baked in) would mean a second match pass for a corner case\n // the caller opted into by gluing unpadded tokens.\n const fromIndices = !runValues.has(name) && groupIndices?.[name];\n /* c8 ignore next */\n const position = fromIndices ? fromIndices[0] : (match.index ?? 0) + consumed;\n parts.push({ token, raw, position });\n consumed += raw.length;\n }\n return parts;\n}\n\n// compileParser: pre-compiles a format string into an object whose\n// parse()/safeParse()/parseToParts() methods skip the per-call\n// pattern-cache lookup. The patternCache in this module means a plain\n// parse(fmt, input) call already pays only a Map lookup after the first\n// call, so compileParser is mostly an ergonomics affordance — useful\n// for callers who want to hold the compiled parser explicitly (e.g. to\n// inspect the pattern via the .pattern property).\nexport interface CompiledParser {\n parse(input: string, options?: NumberingParseOptions): unknown;\n safeParse(input: string, options?: NumberingParseOptions): SafeParseResult;\n tryParse(input: string, options?: NumberingParseOptions): unknown | undefined;\n parseToParts(input: string, options?: NumberingParseOptions): ParsedPart[];\n readonly formatStr: string;\n readonly pattern: CapturingPattern;\n}\n\nexport function compileParser(formatStr: string, options: NumberingParseOptions = {}): CompiledParser {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n // Pre-compile against the default locale; per-call locales will\n // re-resolve via getPattern() if they differ. Most callers use one\n // locale consistently, so pre-compiling against the default keeps\n // the fast path fast.\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n return {\n formatStr,\n pattern,\n parse(input: string, opts: NumberingParseOptions = {}) {\n return parse(formatStr, input, { locale, ...opts });\n },\n safeParse(input: string, opts: NumberingParseOptions = {}) {\n return safeParse(formatStr, input, { locale, ...opts });\n },\n tryParse(input: string, opts: NumberingParseOptions = {}) {\n return tryParse(formatStr, input, { locale, ...opts });\n },\n parseToParts(input: string, opts: NumberingParseOptions = {}) {\n return parseToParts(formatStr, input, { locale, ...opts });\n },\n };\n}","import { type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { canonicalCacheKey, normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\n// A Temporal.Duration doesn't sit on a calendar — it has no year/month/day\n// position the way a PlainDate does — so the date/time token set in\n// tokens.ts doesn't apply. This is a separate token table for the same\n// reason parseRelative() lives in its own module: a different surface\n// deserves its own grammar, not a bolt-on to the existing one.\n//\n// Token design: each unit has three forms, in increasing verbosity.\n// `y` — numeric value, no unit text (\"2\")\n// `yy` — short unit suffix, plural-aware (\"2y\", or \"1y\" — see below)\n// `yyy` — full unit word, plural-aware (\"2 years\", \"1 year\")\n// Same for months (`o`/`oo`/`ooo` — `o` not `M` because the duration's\n// own field name in Temporal is \"months\" and the long-form output should\n// read \"month\"/\"months\", but the *token* letter is arbitrary; `o` is\n// chosen so it doesn't visually collide with `m` (minutes) or `M`\n// (months in date format) when a reader skims both tables side by side).\n//\n// Locale-aware rendering: when a `locale` is passed, the short/long\n// forms delegate to `Intl.NumberFormat` with `style: 'unit'`. That gives\n// us pluralization, native unit names, and the locale's preferred\n// spacing for free, matching the same \"delegate to Intl\" approach\n// `formatDistance` already uses. Without a `locale`, the original\n// English-only hardcoded table is used so existing default output stays\n// byte-identical — this is an additive locale option, not a rewrite of\n// the default rendering path.\n\ntype UnitKey = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface UnitForms {\n // singular and plural suffixes for the long form (e.g. \"year\", \"years\")\n longSingular: string;\n longPlural: string;\n // singular and plural suffixes for the short form (e.g. \"yr\", \"yrs\")\n shortSingular: string;\n shortPlural: string;\n // the property on Temporal.Duration to read for this unit\n field: UnitKey;\n // Intl.NumberFormat unit identifier (used only on the locale-aware path).\n // Confirmed against the current Intl spec — including `millisecond`, which\n // is in fact supported by every engine we target (Node 20+, modern ICU).\n // The task brief flagged this as a possible gap; empirically it isn't.\n intlUnit: string;\n}\n\nconst DURATION_UNITS: Record<string, UnitForms> = {\n y: { longSingular: 'year', longPlural: 'years', shortSingular: 'yr', shortPlural: 'yrs', field: 'years', intlUnit: 'year' },\n o: { longSingular: 'month', longPlural: 'months', shortSingular: 'mo', shortPlural: 'mos', field: 'months', intlUnit: 'month' },\n w: { longSingular: 'week', longPlural: 'weeks', shortSingular: 'wk', shortPlural: 'wks', field: 'weeks', intlUnit: 'week' },\n d: { longSingular: 'day', longPlural: 'days', shortSingular: 'd', shortPlural: 'd', field: 'days', intlUnit: 'day' },\n h: { longSingular: 'hour', longPlural: 'hours', shortSingular: 'h', shortPlural: 'h', field: 'hours', intlUnit: 'hour' },\n m: { longSingular: 'minute', longPlural: 'minutes', shortSingular: 'm', shortPlural: 'm', field: 'minutes', intlUnit: 'minute' },\n s: { longSingular: 'second', longPlural: 'seconds', shortSingular: 's', shortPlural: 's', field: 'seconds', intlUnit: 'second' },\n S: { longSingular: 'millisecond', longPlural: 'milliseconds', shortSingular: 'ms', shortPlural: 'ms', field: 'milliseconds', intlUnit: 'millisecond' },\n};\n\n// Token strings this module recognizes, longest-first for the greedy\n// tokenizer. tokenize() in tokenize.ts already does longest-first via\n// SORTED_TOKEN_STRINGS, but it operates on the *date/time* TOKENS table —\n// not this one. Rather than teach tokenize() about two tables, this\n// module uses its own equivalent scan over DURATION_UNITS keys.\nconst DURATION_TOKEN_STRINGS = Object.keys(DURATION_UNITS)\n .flatMap((single) => [single + single + single, single + single, single])\n .sort((a, b) => b.length - a.length);\n\ntype Piece =\n | { kind: 'token'; value: string; unit: string; form: 'numeric' | 'short' | 'long' }\n | { kind: 'literal'; value: string };\n\n// Same greedy-match shape as tokenize() in tokenize.ts, but with this\n// module's own token set. Quoted-literal handling is identical so the\n// same documented escaping rules carry over: 'at' is a literal, '' is a\n// literal quote.\nfunction tokenizeDuration(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n while (i < format.length) {\n const ch = format[i];\n if (ch === \"'\") {\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\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 if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in duration format string \"${format}\"`);\n }\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = DURATION_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // single letter = numeric, double = short, triple = long\n const unit = match[0]!;\n const form = match.length === 1 ? 'numeric' : match.length === 2 ? 'short' : 'long';\n pieces.push({ kind: 'token', value: match, unit, form });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n}\n\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\n// Reads a unit value off the duration in a way that works for both real\n// Temporal.Duration objects and field bags ({ hours: 2, minutes: 30 }).\n// Temporal.Duration values are numbers, possibly negative for\n// backwards durations; a field bag the caller passes might leave fields\n// off entirely (undefined → treated as 0 for this unit).\nfunction readUnit(duration: Record<string, unknown>, field: UnitKey): number {\n const value = duration[field];\n if (value === undefined || value === null) return 0;\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n // Temporal.Duration values are always numbers, but a hand-built field\n // bag could end up with a string here. Coerce; throw on garbage rather\n // than silently formatting NaN.\n const coerced = Number(value);\n if (!Number.isFinite(coerced)) {\n throw new Error(\n `temporal-fmt: duration field \"${field}\" is not a finite number (got ${String(value)}).`\n );\n }\n return coerced;\n}\n\n// Intl.NumberFormat is expensive to construct, and a typical duration\n// render walks 2-4 units — so a tight loop on the same (locale, unit,\n// display) triple would otherwise rebuild the formatter for every token\n// of every row. Cache by canonical-locale + unit + display, same\n// eviction shape as the other locale-keyed caches in this library\n// (formatterCache in tokens.ts, rtfCache in formatDistance.ts).\nconst unitFormatterCache = new Map<string, Intl.NumberFormat>();\nconst MAX_UNIT_FORMATTER_CACHE_SIZE = 200;\n\nfunction getUnitFormatter(locale: string, intlUnit: string, unitDisplay: 'short' | 'long'): Intl.NumberFormat {\n const key = `${canonicalCacheKey(locale)}|${intlUnit}|${unitDisplay}`;\n const cached = unitFormatterCache.get(key);\n if (cached) return cached;\n if (unitFormatterCache.size >= MAX_UNIT_FORMATTER_CACHE_SIZE) {\n const oldestKey = unitFormatterCache.keys().next().value;\n if (oldestKey !== undefined) unitFormatterCache.delete(oldestKey);\n }\n let formatter: Intl.NumberFormat;\n try {\n formatter = new Intl.NumberFormat(normalizeLocaleTag(locale), {\n style: 'unit',\n unit: intlUnit,\n unitDisplay,\n });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n unitFormatterCache.set(key, formatter);\n return formatter;\n}\n\nexport interface DurationFormatOptions extends FormatOptions {\n /**\n * When true, units whose value is zero are still emitted in the output\n * (e.g. \"0 hours, 30 minutes\" instead of \"30 minutes\"). Default is\n * false: zero-value units are omitted, matching how date-fns's\n * formatDuration works and how most callers rendering a duration to\n * a human would want it.\n */\n showZeroValues?: boolean;\n}\n\n/**\n * Format a Temporal.Duration (or a plain field bag { years, months, ...\n * }) using a duration-specific token string. Token grammar is documented\n * in the README under \"Duration formatting\" — it does NOT reuse the\n * date/time token table, since a duration has no calendar position.\n *\n * Zero-value units are omitted by default; pass { showZeroValues: true }\n * to force them to appear.\n *\n * Unit-name localization: without a `locale`, output is the original\n * English hardcoded singular/plural forms (byte-identical to previous\n * versions). With a `locale`, the short/long forms delegate to\n * `Intl.NumberFormat`'s `style: 'unit'` — same approach `formatDistance`\n * already uses for `Intl.RelativeTimeFormat`. Numeric-only tokens\n * (`y`, `o`, `w`, ...) are not affected by `locale`; they remain ASCII\n * digits, matching the rest of this library's \"numbers stay Western\"\n * convention.\n *\n * @example\n * formatDuration(Temporal.Duration.from({ years: 2, months: 1 }), 'yyy ooo')\n * // \"2 years 1 month\"\n * formatDuration({ hours: 2, minutes: 30 }, 'hhh mmm', { locale: 'fr-FR' })\n * // \"2 heures 30 minutes\"\n */\nexport function formatDuration(\n duration: Record<string, unknown>,\n formatStr: string,\n options: DurationFormatOptions = {},\n): string {\n return renderDurationPieces(duration, formatStr, options).map((p) => p.value).join('');\n}\n\n// One rendered piece of a duration format string: either literal text or\n// a token with its already-rendered value (empty string for a\n// zero-suppressed token, matching formatDurationToParts' documented\n// contract of emitting an empty token part rather than dropping it).\nexport interface RenderedDurationPiece {\n kind: 'literal' | 'token';\n value: string;\n token?: string;\n}\n\n// Shared renderer behind both formatDuration() (joins the pieces) and\n// formatDurationToParts() (returns them). Single source of truth, so the\n// joined string and the parts can never disagree — the old\n// formatDurationToParts re-derived part boundaries by searching the\n// rendered string with indexOf and mis-sliced whenever a literal's text\n// also occurred inside an earlier token's rendered value.\nexport function renderDurationPieces(\n duration: Record<string, unknown>,\n formatStr: string,\n options: DurationFormatOptions = {},\n): RenderedDurationPiece[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: duration format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const showZeroes = options.showZeroValues === true;\n // Undefined locale → keep the original hardcoded English path so\n // existing default output is byte-identical. Any locale string passed\n // (including 'en-US') takes the Intl.NumberFormat path — so an\n // explicit `locale: 'en-US'` will produce Intl's spacing/word choices\n // rather than the hand-rolled English table. That's intentional: the\n // locale-aware path delegates to Intl, the default path doesn't.\n const locale = options.locale;\n const useIntl = locale !== undefined;\n\n const pieces = tokenizeDuration(formatStr);\n const out: RenderedDurationPiece[] = [];\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n out.push({ kind: 'literal', value: piece.value });\n continue;\n }\n\n const unit = DURATION_UNITS[piece.unit];\n // Dead by construction: piece.unit always comes from match[0] where\n // match is a member of DURATION_TOKEN_STRINGS, itself built\n // exhaustively from DURATION_UNITS' own keys — so this lookup can\n // never miss. Kept in case tokenizeDuration's token construction\n // ever diverges from DURATION_UNITS.\n /* c8 ignore start */\n if (!unit) {\n throw new Error(`temporal-fmt: unknown duration token \"${piece.value}\"`);\n }\n /* c8 ignore stop */\n\n const value = readUnit(duration, unit.field);\n if (value === 0 && !showZeroes) {\n // Omit zero-value units entirely. The token's contribution to the\n // output is empty — but this can leave a dangling separator literal\n // (e.g. \"2 hours, \" with nothing after). Callers who want clean\n // output should structure their format string to not put a\n // separator after a unit that might be zero, or use showZeroValues.\n out.push({ kind: 'token', value: '', token: piece.value });\n continue;\n }\n\n if (piece.form === 'numeric') {\n out.push({ kind: 'token', value: String(value), token: piece.value });\n continue;\n }\n\n if (useIntl) {\n // Intl.NumberFormat handles pluralization, native unit names, and\n // locale-appropriate spacing all in one call — no point replicating\n // that here. We pass the value as-is so negative durations\n // (`-1 hour`) round-trip the sign the way Intl expects.\n const unitDisplay = piece.form === 'short' ? 'short' : 'long';\n const formatter = getUnitFormatter(locale!, unit.intlUnit, unitDisplay);\n out.push({ kind: 'token', value: formatter.format(value), token: piece.value });\n continue;\n }\n\n // Default-path English. Plural rule based on absolute value matches\n // what Intl does for unit style on en-US — only |value| === 1 is\n // singular, including for negatives like \"-1 hour\" (which is what\n // Intl produces for en-US too).\n if (piece.form === 'short') {\n out.push({ kind: 'token', value: value + (value === 1 || value === -1 ? unit.shortSingular : unit.shortPlural), token: piece.value });\n } else {\n out.push({ kind: 'token', value: value + ' ' + (value === 1 || value === -1 ? unit.longSingular : unit.longPlural), token: piece.value });\n }\n }\n\n return out;\n}","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { dayOfYear, isGregorianLeapYear } from './isoWeek.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\n// Reads date fields off a Temporal value in the same minimal-shape style\n// as the rest of the library (TemporalLike) — no Temporal factory needed.\n// Works for PlainDate, PlainDateTime, and ZonedDateTime (since they all\n// expose year/month/day). PlainTime has no date fields; formatDistance()\n// between two PlainTime values isn't really meaningful and isn't\n// supported — it'll throw below when the field read fails.\ninterface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction readFields(value: unknown, label: string): DateFieldView {\n if (value === null || typeof value !== 'object') {\n throw new Error(\n `temporal-fmt: formatDistance expects Temporal values, got ${label} = ${String(value)}.`\n );\n }\n const obj = value as Record<string, unknown>;\n // Either year/month/day (a real date) or none of them (a PlainTime);\n // anything in between would be a malformed Temporal-like.\n const hasYear = typeof obj.year === 'number';\n const hasMonth = typeof obj.month === 'number';\n const hasDay = typeof obj.day === 'number';\n if (hasYear !== hasMonth || hasMonth !== hasDay) {\n throw new Error(\n `temporal-fmt: formatDistance got a ${label} with a partial date (some of year/month/day missing). ` +\n `Pass a full Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return {\n year: hasYear ? (obj.year as number) : undefined,\n month: hasMonth ? (obj.month as number) : undefined,\n day: hasDay ? (obj.day as number) : undefined,\n hour: typeof obj.hour === 'number' ? (obj.hour as number) : undefined,\n minute: typeof obj.minute === 'number' ? (obj.minute as number) : undefined,\n second: typeof obj.second === 'number' ? (obj.second as number) : undefined,\n millisecond: typeof obj.millisecond === 'number' ? (obj.millisecond as number) : undefined,\n };\n}\n\n// Reference epoch for ms-since computation. Jan 1, 2000 UTC is the same\n// anchor isoWeek.ts uses for day-of-week arithmetic; reusing it keeps the\n// reasoning localized to one well-known reference date.\nconst REFERENCE_YEAR = 2000;\nconst MS_PER_SECOND = 1000;\nconst MS_PER_MINUTE = 60 * MS_PER_SECOND;\nconst MS_PER_HOUR = 60 * MS_PER_MINUTE;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\n\nfunction daysSinceReference(year: number, month: number, day: number): number {\n let days = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) {\n days += isGregorianLeapYear(y) ? 366 : 365;\n }\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) {\n days -= isGregorianLeapYear(y) ? 366 : 365;\n }\n }\n days += dayOfYear(year, month, day) - 1;\n return days;\n}\n\nfunction toEpochMs(fields: DateFieldView): number {\n // A PlainDate has no time fields — treat as midnight. A PlainDateTime\n // has them all. Comparing two PlainDates is a day-resolution diff;\n // comparing two PlainDateTimes is millisecond-resolution. Mixing the\n // two (one PlainDate, one PlainDateTime) is allowed and just means the\n // PlainDate side is treated as midnight, which is what most callers\n // would intuitively expect (\"distance from this date to this moment\").\n if (fields.year === undefined || fields.month === undefined || fields.day === undefined) {\n throw new Error(\n `temporal-fmt: formatDistance needs a Temporal value with year/month/day fields ` +\n `(PlainDate, PlainDateTime, or ZonedDateTime). A PlainTime or other shape ` +\n `has no anchor date to diff against.`\n );\n }\n const days = daysSinceReference(fields.year, fields.month, fields.day);\n const hour = fields.hour ?? 0;\n const minute = fields.minute ?? 0;\n const second = fields.second ?? 0;\n const millisecond = fields.millisecond ?? 0;\n return days * MS_PER_DAY + hour * MS_PER_HOUR + minute * MS_PER_MINUTE + second * MS_PER_SECOND + millisecond;\n}\n\n// Unit selection cutoffs. Mirrors the rough thresholds date-fns uses,\n// trimmed to the units Intl.RelativeTimeFormat supports in every engine\n// (it accepts 'second' through 'year'; 'quarter' and 'week' support\n// varies, so we skip 'week' here — going days → months directly at the\n// 30-day boundary is simpler and avoids the engine-support cliff).\n//\n// |diff| < 60 s → seconds (\"30 seconds ago\")\n// |diff| < 60 min → minutes (\"12 minutes ago\")\n// |diff| < 24 h → hours (\"3 hours ago\")\n// |diff| < 30 d → days (\"5 days ago\")\n// |diff| < 365 d → months (\"2 months ago\")\n// otherwise → years (\"1 year ago\")\n//\n// 30 days is an approximation of a month — a calendar month is 28-31\n// days, so the boundary is inherently fuzzy. Same with 365 days for a\n// year. Documented as approximations, not exact calendar arithmetic.\n//\n// `cutoffs` on FormatDistanceOptions overrides individual boundaries\n// per call. DEFAULT_CUTOFFS holds the same numbers as the old hardcoded\n// UNIT_CUTOFFS so callers passing nothing see the same output as\n// before — verified byte-for-byte against the pre-change baseline.\nconst DEFAULT_CUTOFFS = {\n seconds: 60, // 60 s\n minutes: 60, // 60 min\n hours: 24, // 24 h\n days: 30, // 30 d\n months: 365, // 365 d (in days, since the months→years boundary is day-resolution)\n} as const;\n\ntype CutoffKey = keyof typeof DEFAULT_CUTOFFS;\n\n// Cutoffs the caller can override. Each value is in the unit's own\n// native scale (seconds for `seconds`, minutes for `minutes`, etc.) —\n// `months` is the exception: it's the day count that bounds the\n// months→years crossover, since \"months\" itself isn't a fixed number\n// of days. This mirrors how the original hardcoded table expressed\n// the same boundary (30 * MS_PER_DAY for days, 365 * MS_PER_DAY for\n// the months cap) and lets callers say \"treat anything under 90 days\n// as months\" rather than \"anything under 3 months as months\" (which\n// would require picking a definition of \"month\").\nexport interface DistanceCutoffs {\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n months?: number;\n}\n\n// Convert the user-facing cutoffs (per-unit numbers) into the ms\n// thresholds the matching loop needs. Validates each value is a\n// positive finite number, and that the boundaries are monotonically\n// non-decreasing in equivalent ms — without that, the smallest-bound\n// branch always wins and a later branch becomes unreachable, which\n// is never what the caller intended.\nfunction resolveCutoffs(user?: DistanceCutoffs): Array<{ maxMs: number; unit: Intl.RelativeTimeFormatUnit }> {\n const merged: Record<CutoffKey, number> = { ...DEFAULT_CUTOFFS, ...user };\n\n for (const key of Object.keys(merged) as CutoffKey[]) {\n const value = merged[key];\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new Error(\n `temporal-fmt: formatDistance cutoff \"${key}\" must be a positive finite number (got ${String(value)}).`\n );\n }\n }\n\n const secondsMs = merged.seconds * MS_PER_SECOND;\n const minutesMs = merged.minutes * MS_PER_MINUTE;\n const hoursMs = merged.hours * MS_PER_HOUR;\n const daysMs = merged.days * MS_PER_DAY;\n // `months` is in days — see DistanceCutoffs comment above.\n const monthsMs = merged.months * MS_PER_DAY;\n\n if (!(secondsMs <= minutesMs && minutesMs <= hoursMs && hoursMs <= daysMs && daysMs <= monthsMs)) {\n throw new Error(\n `temporal-fmt: formatDistance cutoffs must be monotonically non-decreasing in equivalent ms ` +\n `(seconds ≤ minutes ≤ hours ≤ days ≤ months). Got: seconds=${merged.seconds}s, ` +\n `minutes=${merged.minutes}min, hours=${merged.hours}h, days=${merged.days}d, months=${merged.months}d — ` +\n `pick a sequence where each boundary is at least as large as the one before it.`\n );\n }\n\n return [\n { maxMs: secondsMs, unit: 'second' },\n { maxMs: minutesMs, unit: 'minute' },\n { maxMs: hoursMs, unit: 'hour' },\n { maxMs: daysMs, unit: 'day' },\n { maxMs: monthsMs, unit: 'month' },\n ];\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n const key = `${canonicalLocaleKey(locale)}|${numeric}`;\n let rtf = rtfCache.get(key);\n if (rtf) return rtf;\n if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n const oldestKey = rtfCache.keys().next().value;\n if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n }\n try {\n rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n rtfCache.set(key, rtf);\n return rtf;\n}\n\nfunction canonicalLocaleKey(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n return locale;\n }\n}\n\nexport interface FormatDistanceOptions extends FormatOptions {\n /**\n * 'auto' (default) lets Intl.RelativeTimeFormat use natural forms like\n * \"yesterday\"/\"tomorrow\"/\"now\" when the rounded value lands on ±1 or 0.\n * 'always' forces the strict \"1 day ago\"/\"in 1 day\"/\"in 0 seconds\" form.\n */\n numeric?: 'always' | 'auto';\n /**\n * Override the unit-selection boundaries (seconds→minutes,\n * minutes→hours, hours→days, days→months, months→years). Any\n * subset can be supplied; omitted boundaries fall back to the\n * defaults (60s, 60min, 24h, 30d, 365d). Values are in each\n * unit's native scale except `months`, which is in days — see\n * DistanceCutoffs. Throws descriptively on non-monotonic\n * boundaries or non-positive values, rather than producing\n * confusing output downstream.\n */\n cutoffs?: DistanceCutoffs;\n}\n\n/**\n * Returns a human-readable relative-time string describing `date1`\n * relative to `date2`, e.g. \"3 days ago\", \"in 2 hours\", \"now\". Delegates\n * unit names and pluralization to `Intl.RelativeTimeFormat` so the\n * output localizes the same way the rest of the library's locale-aware\n * tokens do.\n *\n * Convention: the result describes `date1`'s position relative to\n * `date2`. `formatDistance(now, threeDaysAgo)` → `\"3 days ago\"` (the\n * past date is described relative to now). `formatDistance(now, twoHoursFromNow)`\n * → `\"in 2 hours\"`. This matches the natural-language reading \"describe\n * the first date as if standing at the second one.\"\n *\n * Unit-selection cutoffs (seconds → minutes → hours → days → months →\n * years) and the rationale for each are documented in the README under\n * \"formatDistance\".\n *\n * @example\n * formatDistance(threeDaysAgo, today) // \"3 days ago\"\n * formatDistance(twoHoursFromNow, today) // \"in 2 hours\"\n * formatDistance(today, today) // \"now\"\n * formatDistance(futureDate, today, {locale:'fr-FR'}) // \"dans 2 jours\"\n */\nexport function formatDistance(\n date1: unknown,\n date2: unknown,\n options: FormatDistanceOptions = {},\n): string {\n const fields1 = readFields(date1, 'date1');\n const fields2 = readFields(date2, 'date2');\n\n // diff > 0: date1 is later than date2 → \"in X units\" (date1 is in\n // the future relative to date2). diff < 0: date1 is earlier than\n // date2 → \"X units ago\". This convention puts date1 in the\n // \"described\" role and date2 in the \"reference\" role, matching\n // natural-language reading: `formatDistance(threeDaysAgo, today)`\n // = \"describe threeDaysAgo as if standing at today\" = \"3 days ago\".\n const diffMs = toEpochMs(fields1) - toEpochMs(fields2);\n const absMs = Math.abs(diffMs);\n\n const unitCutoffs = resolveCutoffs(options.cutoffs);\n let unit: Intl.RelativeTimeFormatUnit = 'year';\n for (const { maxMs, unit: candidateUnit } of unitCutoffs) {\n if (absMs < maxMs) {\n unit = candidateUnit;\n break;\n }\n }\n\n const divisor = unitToMs(unit);\n const rounded = Math.round(diffMs / divisor);\n\n const numeric = options.numeric ?? 'auto';\n const locale = options.locale ?? DEFAULT_LOCALE;\n const rtf = getRtf(locale, numeric);\n return rtf.format(rounded, unit);\n}\n\n/**\n * Same as formatDistance(date, now), where \"now\" is read from the system\n * clock at call time. Convenience wrapper for the common \"how long ago\n * was this\" case, so the caller doesn't have to construct a reference\n * value themselves.\n *\n * Unlike formatRelativeToNow() (in relativeTime.ts), which only needs\n * calendar-day resolution, this reads the full wall-clock time (hour\n * through millisecond) off the system clock — formatDistance's unit\n * selection is ms-resolution, so a date-only \"now\" would misclassify\n * anything under 24 hours old (e.g. \"1 hour ago\" reading as \"in 23\n * hours\" against a midnight-truncated reference). See the test file\n * for a regression case covering exactly that.\n *\n * The reference is captured fresh on every call, not memoized — each\n * call reflects \"now\" at the moment it runs, same expectation\n * formatRelativeToNow() already sets.\n *\n * @example\n * formatDistanceToNow(threeHoursAgo) // \"3 hours ago\"\n * formatDistanceToNow(tomorrow) // \"in 1 day\" (auto: \"tomorrow\")\n */\nexport function formatDistanceToNow(\n date: unknown,\n options: FormatDistanceOptions = {},\n): string {\n const now = new Date();\n const nowFields = {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n hour: now.getHours(),\n minute: now.getMinutes(),\n second: now.getSeconds(),\n millisecond: now.getMilliseconds(),\n };\n return formatDistance(date, nowFields, options);\n}\n\nfunction unitToMs(unit: Intl.RelativeTimeFormatUnit): number {\n switch (unit) {\n case 'second': return MS_PER_SECOND;\n case 'minute': return MS_PER_MINUTE;\n case 'hour': return MS_PER_HOUR;\n case 'day': return MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable: unitToMs is only called\n with `unit` values produced by formatDistance's cutoff loop above,\n which only ever yields 'second'/'minute'/'hour'/'day'/'month'\n (from resolveCutoffs' fixed array) or 'year' (the loop's fallback\n default). 'week' and 'quarter' were never wired into\n resolveCutoffs — formatDistance intentionally skips week-\n resolution output (see the comment above DEFAULT_CUTOFFS) — so\n neither can reach this switch. */\n case 'week': return 7 * MS_PER_DAY;\n /* c8 ignore stop @preserve */\n case 'month': return 30 * MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable, same reason as 'week'\n above: resolveCutoffs never produces 'quarter'. */\n case 'quarter': return 91 * MS_PER_DAY;\n /* c8 ignore stop @preserve */\n case 'year': return 365 * MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable: every unit\n resolveCutoffs can actually produce ('second' through 'month', or\n 'year' as the loop's fallback default) has an explicit case\n above, so this exhaustive-switch default can't fire at runtime.\n It exists so TS accepts the function as total — the compiler\n can't see through the cutoff-array iteration in formatDistance()\n to know the input is already narrowed. */\n default:\n throw new Error(`temporal-fmt: formatDistance hit unhandled unit \"${String(unit)}\".`);\n /* c8 ignore stop @preserve */\n }\n}","// Relative-grammar registration. Extends the existing\n// parseRelative.ts with an extension point for registering additional\n// grammars beyond the four built in (English, Spanish, French, German).\n//\n// The existing parseRelative() detects the language from the locale\n// option and dispatches to the matching grammar. registerRelativeGrammar()\n// lets a caller add a new language without modifying parseRelative.ts.\n//\n// Grammar shape: a list of matchers, each returning either a Temporal\n// field bag describing the resolved date or null. parseRelative tries\n// each matcher in order until one matches. This mirrors the ENGLISH/\n// SPANISH/FRENCH/GERMAN_GRAMMAR structure parseRelative already uses\n// internally.\n\nimport { getTemporal, type TemporalNamespace } from './temporalProvider.js';\n\nexport interface RelativeGrammarMatch {\n // The Temporal fields the matcher resolved. Used to construct a\n // PlainDate / PlainDateTime via Temporal.PlainDate.from().\n year?: number;\n month?: number;\n day?: number;\n // Optional: instead of computing fields directly, return a function\n // that takes the reference date and returns the resolved date. Used\n // for phrases like \"next Tuesday\" that depend on the reference's\n // weekday.\n resolve?: (reference: { year: number; month: number; day: number; dayOfWeek: number }) => { year: number; month: number; day: number };\n}\n\nexport interface RelativeGrammar {\n // Locale language subtag this grammar handles ('en', 'es', etc.).\n // Matched against the `locale` option's language subtag.\n language: string;\n // Matchers in priority order. First match wins.\n matchers: Array<(input: string) => RelativeGrammarMatch | null>;\n}\n\n// Cap on registered grammars. Replacement registration for an existing\n// language never grows the registry, so the cap only gates new languages —\n// the same convention registerLocaleVocab uses for custom vocabularies.\n// Without it, a caller looping registerRelativeGrammar() grows the array\n// (and the per-language scan in tryRegisteredGrammar) without bound.\nconst MAX_REGISTERED_GRAMMARS = 100;\n\nconst registeredGrammars: RelativeGrammar[] = [];\n\nexport function registerRelativeGrammar(grammar: RelativeGrammar): void {\n if (typeof grammar.language !== 'string' || grammar.language.length === 0) {\n throw new Error('temporal-fmt: registerRelativeGrammar requires a non-empty language string.');\n }\n if (!Array.isArray(grammar.matchers) || grammar.matchers.length === 0) {\n throw new Error('temporal-fmt: registerRelativeGrammar requires at least one matcher.');\n }\n // Replace any existing grammar for the same language.\n const existingIdx = registeredGrammars.findIndex((g) => g.language === grammar.language);\n if (existingIdx >= 0) {\n registeredGrammars[existingIdx] = grammar;\n } else {\n if (registeredGrammars.length >= MAX_REGISTERED_GRAMMARS) {\n throw new RangeError(`temporal-fmt: registerRelativeGrammar reached the ${MAX_REGISTERED_GRAMMARS}-grammar limit.`);\n }\n registeredGrammars.push(grammar);\n }\n}\n\n// Called by parseRelative() to try registered grammars before falling\n// back to the built-in ones. Returns the resolved Temporal.PlainDate or\n// null if no registered grammar matched.\nexport function tryRegisteredGrammar(\n language: string,\n input: string,\n reference: { year: number; month: number; day: number; dayOfWeek: number },\n): unknown | null {\n for (const grammar of registeredGrammars) {\n if (grammar.language !== language) continue;\n for (const matcher of grammar.matchers) {\n const match = matcher(input);\n if (match === null) continue;\n const temporal = getTemporal();\n if (match.resolve) {\n const fields = match.resolve(reference);\n return temporal.PlainDate.from(fields, { overflow: 'reject' });\n }\n return temporal.PlainDate.from(\n { year: match.year ?? reference.year, month: match.month ?? reference.month, day: match.day ?? reference.day },\n { overflow: 'reject' },\n );\n }\n }\n return null;\n}\n\n// Returns the list of registered languages (for introspection).\nexport function listRegisteredGrammars(): string[] {\n return registeredGrammars.map((g) => g.language);\n}\n\n// Suppress unused import — TemporalNamespace is referenced by the type\n// signature above (via the return type of tryRegisteredGrammar, which\n// is the result of temporal.PlainDate.from).\nvoid (undefined as unknown as TemporalNamespace);\n","import { getTemporal } from './temporalProvider.js';\nimport { type TemporalNamespace } from './temporalProvider.js';\nimport { tryRegisteredGrammar } from './relativeGrammar.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\n\n// parseRelative resolves common relative-date phrases (\"next Tuesday\",\n// \"in 3 days\", \"March 5th\") against a reference date, returning a\n// Temporal.PlainDate. It's a different subsystem from the token-based\n// parse(): the input is free natural-language text, not a structurally-\n// typed format string, so this module owns its own grammar rather than\n// bolting a natural-language layer onto the existing tokenizer.\n//\n// Per-language grammars: each locale's grammar lives in its own\n// clearly-labeled section below (ENGLISH_GRAMMAR, SPANISH_GRAMMAR,\n// FRENCH_GRAMMAR, GERMAN_GRAMMAR). The vocabulary and phrase patterns\n// are NOT shared across languages — Spanish \"hace 2 semanas\" and\n// French \"il y a 2 semaines\" are different idioms and get their own\n// regexes. What IS shared is the matching engine (iterate matchers in\n// order, first match wins, throw on no match) and the resolution\n// helpers (addDays, addUnits, weekdayOffset, resolveToNextOccurrence).\n// Same approach the humanizing skill calls \"solve each recurring\n// problem once\": the matching strategy is shared, the per-language\n// vocabulary is not.\n//\n// Default locale (no `locale` option) uses English — matches the\n// pre-change behavior byte-for-byte. Passing `locale: 'es'`,\n// `locale: 'fr'`, or `locale: 'de'` (or any locale tag with that\n// language subtag) routes to the corresponding grammar. Unrecognized\n// languages fall back to English rather than throwing — a caller\n// passing `locale: 'it-IT'` still gets *some* result, just from the\n// English grammar. (We could throw instead; the current contract is\n// \"best effort.\" If the caller wanted Italian specifically, they'd\n// pass an Italian grammar via registerLocaleVocab-style extension —\n// not implemented here.)\n\n// Lowercase + expand German umlauts (ä→ae, ö→oe, ü→ue, ß→ss) + strip\n// remaining diacritics. Three things going on at once:\n//\n// 1. Lowercase — case-insensitive matching without forcing the\n// regex flag to do it (also keeps the captured groups lowercase\n// for the resolver's comparisons).\n// 2. Expand German umlaut transliterations. Germans without an\n// umlaut key write \"ä\" as \"ae\" — the standard transliteration.\n// Expanding \"ä\" → \"ae\" (before NFD stripping) means both \"März\"\n// and \"Maerz\" normalize to \"maerz\" and match the same pattern.\n// The reverse (ae → ä) would be wrong: \"Tage\" has \"ae\" as a\n// coincidence, not as an umlaut transliteration. Forward expansion\n// is unambiguous because the source string already has the\n// umlaut — the expansion only affects the matched form.\n// 3. Strip combining marks via NFD. \"Miércoles\" → \"Miercoles\",\n// \"février\" → \"fevrier\", \"août\" → \"aout\". Pairs with the umlaut\n// expansion above so all four languages' accented forms collapse\n// to one canonical unaccented form for matching.\n//\n// Both sides of the match — input string AND the weekday/month name\n// lists — get the same normalization applied, so this is symmetric.\nfunction normalizeForMatch(s: string): string {\n return s.toLowerCase()\n .replace(/ä/g, 'ae')\n .replace(/ö/g, 'oe')\n .replace(/ü/g, 'ue')\n .replace(/ß/g, 'ss')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '');\n}\n\n// Standard Levenshtein edit distance (insert/delete/substitute, cost 1\n// each). Used only by the opt-in fuzzy layer below — the exact matcher\n// pass never calls this. Iterative DP over two rows, not the full\n// matrix, since only the previous row is ever needed.\nfunction levenshtein(a: string, b: string): number {\n /* c8 ignore start @preserve -- unreachable through the only call\n * site (fuzzyCorrectEnglish): `a` is always a word that already\n * failed the `vocabulary.includes(word)` exact-match check, so\n * a === b never holds against any `b` drawn from that same\n * vocabulary; and neither `a` (split from a non-empty normalized\n * string) nor `b` (a vocabulary entry) is ever empty. Kept as\n * general-purpose guards in case levenshtein() gets a second caller\n * later. */\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n /* c8 ignore stop @preserve */\n let prev = new Array(b.length + 1);\n let curr = new Array(b.length + 1);\n for (let j = 0; j <= b.length; j++) prev[j] = j;\n for (let i = 1; i <= a.length; i++) {\n curr[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(\n prev[j] + 1, // deletion\n curr[j - 1] + 1, // insertion\n prev[j - 1] + cost, // substitution\n );\n }\n [prev, curr] = [curr, prev];\n }\n return prev[b.length];\n}\n\n// Max edit distance a fuzzy correction is allowed to bridge. 2 covers\n// the common single-finger-slip and single-transposed-letter typo cases\n// (\"tommorow\" is distance 1 from \"tomorrow\"; \"tuesady\" is distance 2\n// from \"tuesday\" — a transposition costs 2 under simple Levenshtein,\n// which doesn't special-case adjacent-swap as a single edit) without\n// opening the door to correcting a word into something only vaguely\n// similar. Deliberately conservative — this is a typo-tolerance layer,\n// not a \"guess what they meant\" layer; the library's whole design\n// philosophy elsewhere (parse()'s strict-by-default ambiguity handling,\n// the lenient opt-in) is to bound how much guessing is allowed and make\n// the caller ask for it explicitly.\nconst FUZZY_MAX_DISTANCE = 2;\nconst MAX_RELATIVE_INPUT_LENGTH = 4096;\nconst MAX_RELATIVE_WORDS = 32;\n\n// English-only fuzzy vocabulary. Anything a matcher's regex treats as a\n// fixed keyword (not a captured number) belongs here — weekday names,\n// month names (long and short), and the marker words the matchers\n// switch on (today/tomorrow/yesterday/next/last/this/in/ago and the\n// unit words day/week/month/year). Numbers and ordinal suffixes are\n// deliberately excluded: \"5th\" vs \"5tj\" isn't a word-shaped typo this\n// layer is trying to solve, and correcting digits is a much easier way\n// to silently produce a wrong date than correcting a weekday name is.\n//\n// Scoped to English only for this pass — the other three grammars have\n// enough positional/multi-word-marker variation (French's \"il y a\",\n// Spanish's dual pre/post weekday-modifier shapes) that a single\n// word-substitution corrector isn't a good fit for all of them without\n// per-language tuning this change doesn't attempt. parseRelative()\n// throws a clear scope-limited error if { fuzzy: true } is combined\n// with a non-English locale, rather than silently no-op-ing.\nfunction buildEnglishFuzzyVocabulary(): string[] {\n return [\n ...ENGLISH_WEEKDAYS,\n ...ENGLISH_MONTHS,\n ...ENGLISH_MONTH_SHORT,\n 'today', 'tomorrow', 'yesterday',\n 'next', 'last', 'this',\n 'in', 'ago',\n 'day', 'days', 'week', 'weeks', 'month', 'months', 'year', 'years',\n ].map(normalizeForMatch);\n}\n\n// Lazily built and cached — the vocabulary is static per process, no\n// need to rebuild the normalized list on every fuzzy call.\nlet englishFuzzyVocabulary: readonly string[] | undefined;\nfunction getEnglishFuzzyVocabulary(): readonly string[] {\n if (!englishFuzzyVocabulary) englishFuzzyVocabulary = Object.freeze(buildEnglishFuzzyVocabulary());\n return englishFuzzyVocabulary;\n}\n\n// Attempts a word-level fuzzy correction of `normalized` against the\n// English vocabulary, returning a corrected string or undefined if no\n// correction was needed/possible. Tokenizes on whitespace; each token\n// that isn't already an exact vocabulary word (or a pure number/ordinal,\n// which passes through untouched) gets replaced with its closest\n// vocabulary match, if one exists within FUZZY_MAX_DISTANCE. If a token\n// has no sufficiently-close vocabulary match, it's left as-is — the\n// re-attempted exact match will then fail on that token same as before,\n// which is the correct outcome for \"this word isn't a typo of anything\n// we know, it's just not a phrase we support.\"\nfunction fuzzyCorrectEnglish(normalized: string): string | undefined {\n const vocabulary = getEnglishFuzzyVocabulary();\n const words = normalized.split(' ');\n if (normalized.length > MAX_RELATIVE_INPUT_LENGTH || words.length > MAX_RELATIVE_WORDS) {\n throw new RangeError(\n `temporal-fmt: parseRelative fuzzy input is too large (maximum ${MAX_RELATIVE_INPUT_LENGTH} characters and ${MAX_RELATIVE_WORDS} words).`,\n );\n }\n let changed = false;\n const corrected = words.map((word) => {\n // Pure digits, or digits with an ordinal suffix (st/nd/rd/th) —\n // never touched by fuzzy correction. See the vocabulary comment\n // above for why.\n if (/^\\d+(?:st|nd|rd|th)?$/.test(word)) return word;\n // Already an exact vocabulary word — nothing to correct.\n if (vocabulary.includes(word)) return word;\n\n let best: string | undefined;\n let bestDist = FUZZY_MAX_DISTANCE + 1;\n for (const candidate of vocabulary) {\n const dist = levenshtein(word, candidate);\n if (dist < bestDist) {\n bestDist = dist;\n best = candidate;\n }\n }\n if (best !== undefined && bestDist <= FUZZY_MAX_DISTANCE) {\n changed = true;\n return best;\n }\n return word;\n });\n\n return changed ? corrected.join(' ') : undefined;\n}\n\ninterface TemporalFieldBag {\n year?: number;\n month?: number;\n day?: number;\n dayOfWeek?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction readYear(reference: TemporalFieldBag): number {\n if (typeof reference.year !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing year.');\n }\n return reference.year;\n}\nfunction readMonth(reference: TemporalFieldBag): number {\n if (typeof reference.month !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing month.');\n }\n return reference.month;\n}\nfunction readDay(reference: TemporalFieldBag): number {\n if (typeof reference.day !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing day.');\n }\n return reference.day;\n}\n\nfunction extractDayOfWeek(reference: TemporalFieldBag): number {\n const dow = reference.dayOfWeek;\n if (typeof dow !== 'number') {\n throw new Error(\n 'temporal-fmt: parseRelative needs a reference date exposing dayOfWeek (a Temporal.PlainDate / PlainDateTime / ZonedDateTime).'\n );\n }\n return dow;\n}\n\n// Build an alternation regex fragment from a name list, normalized for\n// matching. Longest-first so \"September\" wins over \"Sep\" in the same\n// match position. The caller is responsible for any wrapping groups\n// or anchors — this just produces the inner alternation.\nfunction nameAlternation(names: string[]): string {\n const normalized = names.map(normalizeForMatch);\n // Dedupe — some locales render the long and short form identically\n // (English \"May\" is the same in both MMMM and MMM lists). Keep both\n // in the alternation rather than deduping, since duplicates in a\n // regex alternation don't change behavior and the dedupe would just\n // be a micro-optimization.\n // Sort longest-first so a longer name wins over its own prefix.\n const sorted = [...normalized].sort((a, b) => b.length - a.length);\n // Escape regex metacharacters — names shouldn't contain any, but\n // belt-and-braces avoids a future \"M+1\" month name from breaking\n // the alternation silently.\n return sorted.map((n) => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')).join('|');\n}\n\n// Resolves to a PlainDate by adding `days` to the reference. Used for\n// weekday refs and the today/tomorrow/yesterday family.\nfunction buildPlainDate(temporal: { PlainDate: { from: (fields: Record<string, number>, options?: { overflow?: 'constrain' | 'reject' }) => unknown } }, year: number, month: number, day: number): unknown {\n return temporal.PlainDate.from({ year, month, day }, { overflow: 'reject' });\n}\n\nfunction addDays(\n temporal: { PlainDate: { from: (fields: Record<string, number>) => unknown } },\n reference: TemporalFieldBag,\n days: number,\n): unknown {\n // Build the reference as a real PlainDate so we can use Temporal's\n // own add() for date arithmetic — calendar-correct (handles month\n // boundaries, leap years, etc.) so we don't have to reimplement it.\n const refDate = temporal.PlainDate.from({\n year: readYear(reference),\n month: readMonth(reference),\n day: readDay(reference),\n }) as { add: (duration: Record<string, number>) => unknown };\n return refDate.add({ days });\n}\n\nfunction addUnits(\n temporal: { PlainDate: { from: (fields: Record<string, number>) => unknown } },\n reference: TemporalFieldBag,\n count: number,\n unit: 'day' | 'week' | 'month' | 'year',\n): unknown {\n const refDate = temporal.PlainDate.from({\n year: readYear(reference),\n month: readMonth(reference),\n day: readDay(reference),\n }) as { add: (duration: Record<string, number>) => unknown };\n const duration: Record<string, number> = {};\n if (unit === 'day') duration.days = count;\n if (unit === 'week') duration.weeks = count;\n if (unit === 'month') duration.months = count;\n if (unit === 'year') duration.years = count;\n return refDate.add(duration);\n}\n\n// \"next X\" — strictly future next occurrence. Said on a Tuesday:\n// \"next Tuesday\" = 7 days from now, not today. Rationale: \"this\n// Tuesday\" handles the same-week case, so \"next Tuesday\" staying\n// strictly-future gives the two phrases distinct, non-overlapping\n// meanings. Documented in README \"parseRelative\".\n//\n// \"last X\" — strictly past most recent occurrence. Symmetric to \"next\".\n//\n// \"this X\" — the X of the current ISO week (Mon..Sun). Can land on\n// today, past, or future depending on where in the week today falls.\n//\n// All three resolve the same way across the four supported languages\n// (en/es/fr/de): the equivalent idiom in each language maps to one of\n// these three rel words. The natural-language reading is consistent\n// — \"next Tuesday\" said on a Tuesday means next week's Tuesday in\n// every language we support.\nfunction weekdayOffset(rel: 'next' | 'last' | 'this', refDow: number, targetDow: number): number {\n // refDow and targetDow are both 1..7 (ISO Mon=1..Sun=7).\n if (rel === 'next') {\n let diff = targetDow - refDow;\n if (diff <= 0) diff += 7; // 0 (today) → 7, negative → wrap forward\n return diff; // 1..7\n }\n if (rel === 'last') {\n let diff = targetDow - refDow;\n if (diff >= 0) diff -= 7; // 0 (today) → -7, positive → wrap backward\n return diff; // -7..-1\n }\n // rel === 'this' — X of the current week. Can be today, past, or future.\n return targetDow - refDow; // -6..+6\n}\n\n// \"March 5th\" without a year → resolve to the next occurrence of that\n// month/day. If today is that date, return today. Otherwise, if it's\n// already past this year, return next year's occurrence.\n//\n// \"Next occurrence\" semantics: future-leaning. Documented in README\n// as the chosen behavior. The alternative (\"nearest in time, past or\n// future\") would mean \"March 5th\" said on March 6 returns yesterday —\n// counterintuitive for the typical \"next birthday\"/\"next deadline\"\n// use case this kind of phrase tends to drive.\nfunction resolveToNextOccurrence(\n temporal: {\n PlainDate: {\n from: (fields: Record<string, number>, options?: { overflow?: 'constrain' | 'reject' }) => unknown;\n compare?: (one: unknown, two: unknown) => number;\n };\n },\n reference: TemporalFieldBag,\n month: number,\n day: number,\n): unknown {\n const refYear = readYear(reference);\n const refDate = temporal.PlainDate.from({\n year: refYear,\n month: readMonth(reference),\n day: readDay(reference),\n });\n // PlainDate.compare is a static method on the namespace, not an\n // instance method — that's why it's called off temporal.PlainDate\n // rather than off an instance below. Checked outside the try/catch\n // below on purpose: that block's catch is scoped to Feb-29-style\n // overflow errors from from(), and silently retries with next year's\n // date. A missing-compare error thrown from inside that try would get\n // caught by the same handler and swallowed by that retry (next year's\n // from() usually succeeds), masking the real problem instead of\n // surfacing it.\n if (!temporal.PlainDate.compare) {\n throw new Error('temporal-fmt: parseRelative needs Temporal.PlainDate.compare to resolve month-day phrases; the active implementation does not expose it.');\n }\n // try this year first; if Feb 29 in a non-leap year, Temporal will\n // throw via overflow: 'reject' — fall through to next year.\n try {\n const thisYear = temporal.PlainDate.from({ year: refYear, month, day }, { overflow: 'reject' });\n // Returns -1 / 0 / 1.\n if (temporal.PlainDate.compare(thisYear, refDate) >= 0) {\n // today or in the future → use this year's occurrence\n return thisYear;\n }\n // already past this year → next year's occurrence\n return temporal.PlainDate.from({ year: refYear + 1, month, day }, { overflow: 'reject' });\n } catch (err) {\n // Feb 29 in a non-leap year, or similar. Try next year — if the\n // caller asked for \"Feb 29\" said in a non-leap year, next year may\n // also not be leap, in which case the inner from() will throw and\n // surface a clear error rather than silently landing on Feb 28.\n try {\n return temporal.PlainDate.from({ year: refYear + 1, month, day }, { overflow: 'reject' });\n } catch {\n throw new Error(\n `temporal-fmt: parseRelative can't resolve month ${month} day ${day} — ` +\n `it isn't a valid date in either ${refYear} or ${refYear + 1}. ` +\n `Original error: ${(err as Error).message}`\n );\n }\n }\n}\n\n// Each language provides a list of matchers. The shared engine\n// iterates them in order and returns the first match. A matcher is a\n// (regex, resolver) pair — the regex captures the structural pieces\n// (count, unit name, weekday name, etc.), the resolver turns them\n// into a Temporal.PlainDate using the shared helpers above.\ninterface ResolveContext {\n temporal: TemporalNamespace;\n reference: TemporalFieldBag;\n}\n\ninterface Matcher {\n pattern: RegExp;\n resolve: (match: RegExpMatchArray, ctx: ResolveContext) => unknown;\n}\n\ninterface RelativeDateGrammar {\n // Single-line description of supported phrase classes, included in\n // the \"doesn't recognize\" error so the caller knows what to try.\n supportedHint: string;\n // Ordered matchers; first match wins.\n matchers: Matcher[];\n}\n\nfunction weekdayIndexFromName(name: string, names: string[]): number {\n const normalized = normalizeForMatch(name);\n const idx = names.findIndex((n) => normalizeForMatch(n) === normalized);\n /* c8 ignore start @preserve -- unreachable: every caller passes a\n * name captured by nameAlternation(names), which builds its regex\n * alternation from this same list under the same normalizeForMatch\n * transform. The regex can't match anything findIndex() wouldn't\n * also find here. Defensive throw kept for the type checker and as\n * a tripwire if that invariant ever breaks. Ignore comment sits on\n * the `if` itself, not just the throw inside it — c8 tracks the\n * branch outcome (was the true side ever taken) separately from the\n * statements inside, so excluding only the inner throw still leaves\n * the branch itself flagged as uncovered. */\n /* c8 ignore next */\n if (idx < 0) {\n throw new Error(`temporal-fmt: parseRelative internal error — weekday \"${name}\" not in names list.`);\n }\n /* c8 ignore stop @preserve */\n return idx + 1;\n}\n\nfunction monthIndexFromName(name: string, longNames: string[], shortNames: string[] = []): number {\n const normalized = normalizeForMatch(name);\n const longIdx = longNames.findIndex((n) => normalizeForMatch(n) === normalized);\n if (longIdx >= 0) return longIdx + 1;\n const shortIdx = shortNames.findIndex((n) => normalizeForMatch(n) === normalized);\n /* c8 ignore next */\n if (shortIdx >= 0) return shortIdx + 1;\n /* c8 ignore start @preserve -- unreachable: every caller passes a name\n * captured by nameAlternation([...longNames, ...shortNames]), so the\n * regex alternation and these two findIndex() scans are built from\n * the same lists under the same normalizeForMatch transform. Kept as\n * a tripwire for the type checker in case that invariant ever breaks. */\n throw new Error(`temporal-fmt: parseRelative internal error — month \"${name}\" not in names list.`);\n /* c8 ignore stop @preserve */\n /* c8 ignore next -- defensive block is unreachable by construction. */\n}\n\n// English grammar — also the default when no locale is supplied. The\n// regexes here are the same ones the pre-change parseRelative() used\n// inline; they've just been pulled into a matcher list so the matching\n// engine can iterate them the same way it does for es/fr/de.\nconst ENGLISH_WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];\nconst ENGLISH_MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nconst ENGLISH_MONTH_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\nconst ENGLISH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Supported: weekday refs (\"next Tuesday\"), day offsets (\"today\"/\"tomorrow\"/\"yesterday\"), unit offsets (\"in 3 days\", \"2 weeks ago\"), and month-day (\"March 5th\").',\n matchers: [\n // today / tomorrow / yesterday — exact match, most rigid shape.\n // Checked first because they're the most common phrase class.\n {\n pattern: /^today$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^tomorrow$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^yesterday$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"next/last/this Tuesday\". Capture the relative\n // word and the weekday name separately, then resolve via +7 / -7.\n {\n pattern: new RegExp(`^(next|last|this)\\\\s+(${nameAlternation(ENGLISH_WEEKDAYS)})$`, 'i'),\n resolve: (m, ctx) => {\n const rel = m[1]!.toLowerCase() as 'next' | 'last' | 'this';\n const targetDow = weekdayIndexFromName(m[2]!, ENGLISH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // unit offsets: \"in 3 days\", \"2 weeks ago\", \"in 1 month\", \"1 year ago\".\n // Two shapes share this matcher — leading \"in\" (future) or trailing\n // \"ago\" (past). Both lead with <number> <unit>.\n {\n pattern: /^(in\\s+)?(\\d+)\\s+(day|week|month|year)s?(?:\\s+ago)?$/i,\n resolve: (m, ctx) => {\n const inPrefix = m[1];\n const count = m[2]!;\n const unitName = m[3]!.toLowerCase() as 'day' | 'week' | 'month' | 'year';\n // Disambiguate future vs past: \"in N units\" is always future; \"N\n // units ago\" is always past. A bare \"N units\" without either is\n // a sign error — throw, since neither direction is implied and\n // guessing is exactly what this library refuses to do.\n const hasIn = !!inPrefix;\n const hasAgo = /\\bago\\b/i.test(m[0]!);\n if (!hasIn && !hasAgo) {\n throw new Error(\n `temporal-fmt: parseRelative can't tell whether \"${m[0]}\" is past or future — ` +\n `use \"in ${count} ${unitName}s\" or \"${count} ${unitName}s ago\".`\n );\n }\n const sign = hasIn ? 1 : -1;\n return addUnits(ctx.temporal, ctx.reference, sign * Number(count), unitName);\n },\n },\n // month-day without year, e.g. \"March 5th\", \"March 5\", \"March 5, 2026\"\n // (year is ignored if present). The ordinal suffix is decorative —\n // strip it. The day-of-month is a 1- or 2-digit number.\n {\n pattern: new RegExp(`^(${nameAlternation([...ENGLISH_MONTHS, ...ENGLISH_MONTH_SHORT])})\\\\s+(\\\\d{1,2})(?:st|nd|rd|th)?(?:,?\\\\s*(\\\\d{4}))?$`, 'i'),\n resolve: (m, ctx) => {\n const month = monthIndexFromName(m[1]!, ENGLISH_MONTHS, ENGLISH_MONTH_SHORT);\n const day = Number(m[2]!);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// Spanish grammar. Distinct phrase shapes from English:\n// - \"hace 2 semanas\" (past unit offset) — distinct word \"hace\", not\n// the English \"X units ago\" suffix shape.\n// - \"5 de marzo\" — day-first, with required \"de\" between day and\n// month, opposite of English \"March 5\".\n// - \"el próximo lunes\" / \"lunes pasado\" — weekday ref has the relative\n// word sometimes as a prefix with article, sometimes as a suffix.\n//\n// All four phrase classes the English grammar supports have Spanish\n// equivalents here.\nconst SPANISH_WEEKDAYS = ['lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado', 'domingo'];\nconst SPANISH_MONTHS = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];\n// Spanish month abbreviations vary by region (\"set\" vs \"sept\" for\n// September). Sticking with the most common ICU form per locale.\nconst SPANISH_MONTH_SHORT = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sept', 'oct', 'nov', 'dic'];\n\nconst SPANISH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Frases soportadas: referencias de día de la semana (\"el próximo lunes\", \"el lunes pasado\", \"este martes\"), offsets de día (\"hoy\"/\"mañana\"/\"ayer\"), offsets de unidad (\"en 3 días\", \"hace 2 semanas\"), y mes-día (\"5 de marzo\").',\n matchers: [\n {\n pattern: /^hoy$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^ma[ñn]ana$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^ayer$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references. Spanish has three common shapes, all meaning\n // the same thing per rel word:\n // \"el próximo lunes\" / \"próximo lunes\" → next\n // \"el lunes próximo\" → next (suffix variant)\n // \"el lunes pasado\" / \"lunes pasado\" → last\n // \"este lunes\" → this\n // Capture the rel word from whichever shape matched, then resolve\n // the same way English does.\n {\n pattern: new RegExp(\n `^(?:el\\\\s+)?(?:(pr[oó]ximo|pasado)\\\\s+(${nameAlternation(SPANISH_WEEKDAYS)})|(${nameAlternation(SPANISH_WEEKDAYS)})\\\\s+(pr[oó]ximo|pasado)|este\\\\s+(${nameAlternation(SPANISH_WEEKDAYS)}))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let relWord: string;\n let weekdayName: string;\n if (m[1] && m[2]) {\n // prefix variant: \"próximo lunes\" / \"pasado lunes\"\n relWord = m[1]!;\n weekdayName = m[2]!;\n } else if (m[3] && m[4]) {\n // suffix variant: \"lunes próximo\" / \"lunes pasado\"\n weekdayName = m[3]!;\n relWord = m[4]!;\n } else {\n // \"este lunes\"\n relWord = 'este';\n weekdayName = m[5]!;\n }\n const rel = normalizeForMatch(relWord) === 'proximo' ? 'next' : relWord.toLowerCase() === 'este' ? 'this' : 'last';\n const targetDow = weekdayIndexFromName(weekdayName, SPANISH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // unit offsets. Spanish uses distinct markers:\n // \"en 3 días\" / \"dentro de 3 días\" → future\n // \"hace 2 semanas\" → past\n // Both shapes lead with the marker, unlike English where one\n // shape leads and the other trails.\n {\n pattern: new RegExp(\n `^(?:(en|dentro\\\\s+de)\\\\s+(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?|(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?\\\\s+(hace))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let count: string;\n let unitWord: string;\n let sign: number;\n if (m[1] && m[2] && m[3]) {\n // future: \"en 3 días\" / \"dentro de 3 días\"\n count = m[2]!;\n unitWord = m[3]!;\n sign = 1;\n } else {\n // past: \"2 semanas hace\" — but the more natural Spanish is\n // \"hace 2 semanas\" (marker first). The regex above only\n // matches \"N units hace\"; that's a less common but\n // grammatical alternative. The primary past form \"hace N\n // unidades\" is matched by the next matcher.\n count = m[4]!;\n unitWord = m[5]!;\n sign = -1;\n }\n const unit = unitWordToEnglish(unitWord);\n return addUnits(ctx.temporal, ctx.reference, sign * Number(count), unit);\n },\n },\n // \"hace 2 semanas\" — the primary past-offset shape. Matcher kept\n // separate from the future \"en/dentro de\" matcher above because\n // Spanish word order differs between the two (marker-first for\n // past, marker-first for future too, but with different markers).\n {\n pattern: new RegExp(`^hace\\\\s+(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // bare \"N <unit>\" without \"en\" / \"hace\" — throw, since direction\n // is ambiguous. Matched AFTER the directional shapes so the\n // directional ones win when their marker is present.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative no puede decidir si \"${m[0]}\" es pasado o futuro — ` +\n `usa \"en ${m[1]} ${m[2]}s\" o \"hace ${m[1]} ${m[2]}s\".`\n );\n },\n },\n // month-day: \"5 de marzo\" (day first), \"5 de marzo de 2026\" (year\n // ignored if present). Day is 1-2 digits, with optional trailing\n // \".\" (rare in Spanish but tolerated).\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\.?\\\\s+de\\\\s+(${nameAlternation([...SPANISH_MONTHS, ...SPANISH_MONTH_SHORT])})(?:\\\\s+de\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, SPANISH_MONTHS, SPANISH_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// French grammar. Distinct from English/Spanish:\n// - \"il y a 2 semaines\" (past marker is a 3-word phrase, not a single word).\n// - \"mardi prochain\" — relative word is a SUFFIX on the weekday,\n// opposite of English \"next Tuesday\".\n// - \"5 mars\" — day-first, no \"de\" between day and month (unlike\n// Spanish's \"5 de marzo\").\nconst FRENCH_WEEKDAYS = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'];\nconst FRENCH_MONTHS = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'];\nconst FRENCH_MONTH_SHORT = ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'];\n\nconst FRENCH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Phrases prises en charge : références de jour de la semaine (\"lundi prochain\", \"mardi dernier\", \"ce mercredi\"), décalages de jour (\"aujourd\\'hui\"/\"demain\"/\"hier\"), décalages d\\'unité (\"dans 3 jours\", \"il y a 2 semaines\"), et mois-jour (\"5 mars\").',\n matchers: [\n {\n pattern: /^aujourd'hui$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^demain$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^hier$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"lundi prochain\" (next), \"lundi dernier\"\n // (last), \"ce lundi\" (this). The relative word follows the\n // weekday for next/last, precedes it for this.\n {\n pattern: new RegExp(\n `^(?:(${nameAlternation(FRENCH_WEEKDAYS)})\\\\s+(prochain|dernier)|ce\\\\s+(${nameAlternation(FRENCH_WEEKDAYS)}))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let rel: 'next' | 'last' | 'this';\n let weekdayName: string;\n if (m[1] && m[2]) {\n weekdayName = m[1]!;\n rel = m[2]!.toLowerCase() === 'prochain' ? 'next' : 'last';\n } else {\n rel = 'this';\n weekdayName = m[3]!;\n }\n const targetDow = weekdayIndexFromName(weekdayName, FRENCH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // past: \"il y a 2 semaines\" — multi-word marker.\n {\n pattern: new RegExp(`^il\\\\s+y\\\\s+a\\\\s+(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // future: \"dans 3 jours\"\n {\n pattern: new RegExp(`^dans\\\\s+(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, count, unit);\n },\n },\n // bare \"N <unit>\" without marker — throw.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative ne peut pas déterminer si \"${m[0]}\" est passé ou futur — ` +\n `utilisez \"dans ${m[1]} ${m[2]}s\" ou \"il y a ${m[1]} ${m[2]}s\".`\n );\n },\n },\n // month-day: \"5 mars\" (day first, no separator word). Year ignored.\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\s+(${nameAlternation([...FRENCH_MONTHS, ...FRENCH_MONTH_SHORT])})(?:\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, FRENCH_MONTHS, FRENCH_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// German grammar. Distinct from the others:\n// - Unit words pluralize with -n in the dative after \"in\"/\"vor\":\n// \"in 3 Tagen\" (vs nominative \"3 Tage\"). The regex accepts both\n// since callers may not always get the case right, and accepting\n// both doesn't introduce ambiguity.\n// - \"nächsten Dienstag\" — relative word precedes weekday as a\n// declined adjective, with the weekday capitalized as a noun.\n// - \"5. März\" — day-first with a period after the day number,\n// matching German date convention.\nconst GERMAN_WEEKDAYS = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];\nconst GERMAN_MONTHS = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];\nconst GERMAN_MONTH_SHORT = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];\n\nconst GERMAN_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Unterstützte Phrasen: Wochentag-Bezüge (\"nächsten Dienstag\", \"letzten Freitag\", \"diesen Montag\"), Tages-Offsets (\"heute\"/\"morgen\"/\"gestern\"), Einheiten-Offsets (\"in 3 Tagen\", \"vor 2 Wochen\"), und Monat-Tag (\"5. März\").',\n matchers: [\n {\n pattern: /^heute$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^morgen$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^gestern$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"nächsten Dienstag\" (next), \"letzten\n // Dienstag\" (last), \"diesen Dienstag\" (this). Adjective precedes\n // the weekday; case varies (nächsten/letzten/diesen all take the\n // weak-declension -en/-en/-en ending when no article, which is\n // convenient — one regex alternation covers all three.\n {\n pattern: new RegExp(`^(n(?:ae|a|ä)chsten|letzten|diesen)\\\\s+(${nameAlternation(GERMAN_WEEKDAYS)})$`, 'i'),\n resolve: (m, ctx) => {\n const adj = normalizeForMatch(m[1]!);\n const rel: 'next' | 'last' | 'this' = (adj === 'nachsten' || adj === 'naechsten') ? 'next' : adj === 'diesen' ? 'this' : 'last';\n const targetDow = weekdayIndexFromName(m[2]!, GERMAN_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // past: \"vor 2 Wochen\"\n {\n pattern: new RegExp(`^vor\\\\s+(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // future: \"in 3 Tagen\"\n {\n pattern: new RegExp(`^in\\\\s+(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, count, unit);\n },\n },\n // bare \"N <unit>\" without \"in\" or \"vor\" — throw.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative kann nicht erkennen, ob \"${m[0]}\" Vergangenheit oder Zukunft ist — ` +\n `verwende \"in ${m[1]} ${m[2]}\" oder \"vor ${m[1]} ${m[2]}\".`\n );\n },\n },\n // month-day: \"5. März\" (day-first with period). Year ignored.\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\.?\\\\s+(${nameAlternation([...GERMAN_MONTHS, ...GERMAN_MONTH_SHORT, 'Maerz', 'Maer'])})(?:\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, GERMAN_MONTHS, GERMAN_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\nconst GRAMMARS: Record<string, RelativeDateGrammar> = {\n en: ENGLISH_GRAMMAR,\n es: SPANISH_GRAMMAR,\n fr: FRENCH_GRAMMAR,\n de: GERMAN_GRAMMAR,\n};\n\n// Pick a grammar for a locale. Falls back to English for languages we\n// don't have a grammar for (Italian, Portuguese, etc.) rather than\n// throwing — best-effort, matches how formatDistance treats unknown\n// locales (Intl.RelativeTimeFormat accepts them; here we fall back to\n// English so the call still succeeds with reasonable output).\nfunction grammarForLocale(locale: string | undefined): RelativeDateGrammar {\n if (!locale) return ENGLISH_GRAMMAR;\n try {\n // Intl.Locale requires BCP-47 hyphens — same normalization as the\n // other locale-handling code in this library.\n const tag = new Intl.Locale(locale.replace(/_/g, '-'));\n const lang = tag.language.toLowerCase();\n return GRAMMARS[lang] ?? ENGLISH_GRAMMAR;\n } catch {\n // Malformed locale tag — fall back to English. The actual Intl\n // call downstream will throw if the tag is genuinely unusable,\n // which is a better surface for that error than here.\n return ENGLISH_GRAMMAR;\n }\n}\n\n// Language subtag of a locale option, for dispatching to grammars\n// registered via registerRelativeGrammar(). Mirrors grammarForLocale's\n// normalization and fallback.\nfunction languageOf(locale: string | undefined): string {\n if (!locale) return 'en';\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).language.toLowerCase();\n } catch {\n return 'en';\n }\n}\n\n// Map a per-language unit word back to the English unit name the\n// shared addUnits() helper expects. Each language contributes its own\n// regex alternation of unit words; this function normalizes the\n// captured word back to a unit enum value.\nfunction unitWordToEnglish(word: string): 'day' | 'week' | 'month' | 'year' {\n const w = normalizeForMatch(word);\n // English\n /* c8 ignore next */\n if (w === 'day' || w === 'week' || w === 'month' || w === 'year') return w;\n // Spanish (accent-stripped)\n if (w === 'dia') return 'day';\n if (w === 'semana') return 'week';\n if (w === 'mes') return 'month';\n if (w === 'ano' || w === 'año') return 'year';\n // French\n if (w === 'jour') return 'day';\n if (w === 'semaine') return 'week';\n if (w === 'mois') return 'month';\n if (w === 'an' || w === 'annee') return 'year';\n // German\n if (w === 'tag' || w === 'tagen') return 'day';\n if (w === 'woche' || w === 'wochen') return 'week';\n if (w === 'monat' || w === 'monaten') return 'month';\n /* c8 ignore next */\n if (w === 'jahr' || w === 'jahren') return 'year';\n /* c8 ignore start @preserve -- unreachable: every caller passes a word\n * captured by a regex alternation built from exactly these unit-word\n * forms (day/week/month/year across en/es/fr/de), so the branches\n * above already cover every string that can reach this function.\n * Kept as a tripwire for the type checker in case a grammar adds a\n * unit word here without a matching regex update. */\n throw new Error(`temporal-fmt: parseRelative internal error — unrecognized unit word \"${word}\".`);\n /* c8 ignore stop @preserve */\n /* c8 ignore next -- defensive block is unreachable by construction. */\n}\n\n// Reference: kept exported for the unit tests that need it. The other\n// helpers above are not exported since tests only need to drive\n// parseRelative() through the public API + this one constant for the\n// Tuesday-on-Tuesday fixture.\nexport const WEEKDAY_NAMES = ENGLISH_WEEKDAYS;\nexport const MONTH_NAMES = ENGLISH_MONTHS;\n// Exported for the localeVocab-style stub-test pattern in case a future\n// test wants to verify a grammar's name list against Intl's actual\n// output for that locale — same pattern localeVocab.test.js uses to\n// catch a mismatch between what format() produces and what parse()\n// expects.\nexport const SPANISH_MONTH_NAMES = SPANISH_MONTHS;\nexport const FRENCH_MONTH_NAMES = FRENCH_MONTHS;\nexport const GERMAN_MONTH_NAMES = GERMAN_MONTHS;\nexport const SPANISH_WEEKDAY_NAMES = SPANISH_WEEKDAYS;\nexport const FRENCH_WEEKDAY_NAMES = FRENCH_WEEKDAYS;\nexport const GERMAN_WEEKDAY_NAMES = GERMAN_WEEKDAYS;\nvoid WEEKDAY_NAMES;\nvoid MONTH_NAMES;\n\nexport interface ParseRelativeOptions extends FormatOptions {\n /**\n * Opt-in typo tolerance. When the input doesn't match any phrase\n * exactly, retry after correcting individual words (weekday names,\n * month names, and marker words like \"next\"/\"ago\"/\"tomorrow\") that\n * are within a small edit distance of a known word — e.g.\n * \"tommorow\" → \"tomorrow\", \"next tuesady\" → \"next tuesday\". Numbers\n * are never touched.\n *\n * Off by default, matching this library's throw-rather-than-guess\n * philosophy elsewhere (parse()'s `lenient` option is the same shape\n * of opt-in). English only for this option currently — combining\n * `fuzzy: true` with a non-English `locale` throws rather than\n * silently skipping the fuzzy pass.\n *\n * If no correction produces a recognized phrase, throws the same\n * \"doesn't recognize\" error the exact pass would have thrown.\n */\n fuzzy?: boolean;\n}\n\n/**\n * Resolve common relative-date phrases against a reference date,\n * returning a Temporal.PlainDate. Supports English by default, plus\n * Spanish (es), French (fr), and German (de) via the `locale` option.\n *\n * Supported phrase classes (with examples per language):\n *\n * - weekday refs: \"next Tuesday\" / \"el próximo martes\" / \"mardi prochain\" / \"nächsten Dienstag\"\n * - day offsets: \"today\" / \"hoy\" / \"aujourd'hui\" / \"heute\" (and tomorrow/yesterday equivalents)\n * - unit offsets: \"in 3 days\" / \"en 3 días\" / \"dans 3 jours\" / \"in 3 Tagen\"\n * - month-day without year: \"March 5th\" / \"5 de marzo\" / \"5 mars\" / \"5. März\"\n *\n * Ambiguous-case handling is consistent across all four languages:\n * \"next X\" said on X (today is the named weekday) means 7 days from\n * now, not today. \"last X\" said on X means 7 days ago. See README\n * \"parseRelative\" for the documented behavior of each ambiguous case.\n *\n * Throws a descriptive error for any phrase it doesn't recognize in\n * the target language, rather than guessing. A bare \"5 days\" without\n * \"in\" or \"ago\" (or the per-language equivalent) throws with a message\n * pointing at the disambiguation options.\n *\n * Returns a Temporal.PlainDate for every supported phrase. Time-of-day\n * extensions like \"next Tuesday at 3pm\" aren't supported in this pass.\n */\nexport function parseRelative(\n input: string,\n referenceDate: unknown,\n options: ParseRelativeOptions = {},\n): unknown {\n const reference = referenceDate as TemporalFieldBag;\n const temporal = getTemporal();\n\n // Trim, collapse internal whitespace, strip diacritics, and lowercase.\n // Whitespace normalization is shared with the pre-change behavior;\n // diacritic stripping pairs with nameAlternation()'s normalization so\n // \"mi[eé]rcoles\" and \"miercoles\" both match the same pattern.\n // Lowercasing matches the case-insensitive regex flag explicitly so\n // the user's input never gets case-swapped on its way through the\n // matcher (the regexes are 'i'-flagged, but normalizing here keeps\n // the original-trimmed-string echo in error messages predictable).\n const trimmed = (input ?? '').trim().replace(/\\s+/g, ' ');\n if (trimmed.length === 0) {\n throw new Error('temporal-fmt: parseRelative got an empty input string.');\n }\n if (trimmed.length > MAX_RELATIVE_INPUT_LENGTH) {\n throw new RangeError(\n `temporal-fmt: parseRelative input is too large (maximum ${MAX_RELATIVE_INPUT_LENGTH} characters).`,\n );\n }\n // For error messages, keep the user's original spelling (with accents)\n // so the echo in the \"doesn't recognize\" error matches what they\n // typed. The matching itself uses the diacritic-stripped form so\n // \"mi[eé]rcoles\" / \"miercoles\" / \"MI[ÉE]RCOLES\" all match the same\n // pattern without forcing the user to remember the accent.\n const normalized = normalizeForMatch(trimmed);\n\n const grammar = grammarForLocale(options.locale);\n const ctx: ResolveContext = { temporal, reference };\n\n // Registered grammars (registerRelativeGrammar) take precedence over\n // the built-in ones for their language — that's the documented\n // contract in relativeGrammar.ts (\"try registered grammars before\n // falling back to the built-in ones\"). This wiring was missing\n // entirely before: registered grammars were stored and listed but\n // never consulted, so the extension point silently did nothing.\n // Returns null quickly when nothing is registered for the language.\n const registered = tryRegisteredGrammar(languageOf(options.locale), trimmed, {\n year: reference.year as number,\n month: reference.month as number,\n day: reference.day as number,\n dayOfWeek: reference.dayOfWeek as number,\n });\n if (registered !== null) {\n return registered;\n }\n\n const tryMatch = (text: string): unknown | typeof NO_MATCH => {\n for (const matcher of grammar.matchers) {\n const match = text.match(matcher.pattern);\n if (match) {\n return matcher.resolve(match, ctx);\n }\n }\n return NO_MATCH;\n };\n\n const exactResult = tryMatch(normalized);\n if (exactResult !== NO_MATCH) {\n return exactResult;\n }\n\n if (options.fuzzy) {\n // Fuzzy mode is English-only for this pass — see the vocabulary\n // comment above fuzzyCorrectEnglish for why the other three\n // grammars aren't covered. Fail loudly rather than silently\n // skipping the fuzzy attempt, so a caller relying on fuzzy mode\n // for, say, French input doesn't get a confusing \"doesn't\n // recognize\" error with no indication fuzzy matching never ran.\n if (grammar !== ENGLISH_GRAMMAR) {\n throw new Error(\n `temporal-fmt: parseRelative's { fuzzy: true } option currently only supports English. ` +\n `Pass no locale (or locale: 'en'), or omit { fuzzy: true } for this locale.`\n );\n }\n const corrected = fuzzyCorrectEnglish(normalized);\n if (corrected !== undefined) {\n const fuzzyResult = tryMatch(corrected);\n if (fuzzyResult !== NO_MATCH) {\n return fuzzyResult;\n }\n }\n }\n\n throw new Error(\n `temporal-fmt: parseRelative doesn't recognize \"${trimmed}\". ` +\n grammar.supportedHint\n );\n}\n\n// Sentinel distinguishing \"no matcher matched\" from a legitimate\n// resolve() return value of `undefined`/`null` (matchers can throw\n// their own errors, e.g. the ambiguous-direction case, but none\n// currently return a nullish PlainDate — this sentinel just makes the\n// \"no match\" case unambiguous regardless).\nconst NO_MATCH = Symbol('temporal-fmt: parseRelative no match');\n\n// DEFAULT_LOCALE is imported for the option's default-value documentation\n// above (no locale → English). The symbol itself isn't read at runtime\n// here, but the import keeps the dependency edge explicit so future\n// changes to the default-locale constant propagate here too.\nvoid DEFAULT_LOCALE;","// Type guards for Temporal values. The rest of this library works off the\n// duck-typed `TemporalLike` shape (year/month/day/hour/...) rather than\n// `instanceof Temporal.X`, because `instanceof` breaks across polyfill/\n// native boundaries — a polyfill's `Temporal.PlainDate` is a different\n// constructor from the native one, so a `value instanceof Temporal.PlainDate`\n// check would silently fail when the caller is using a polyfill while the\n// guard happens to be looking at native Temporal, or vice versa. Same\n// reasoning as tokens.ts's `intlSupportsNativeTemporal` probe: identity\n// checks don't survive the polyfill/native swap.\n//\n// These guards check structural shape instead: presence of the type-\n// discriminating method each Temporal type carries. Method presence is\n// part of the Temporal spec contract — every implementation, native or\n// polyfill, must expose the same methods on the same types — so it's a\n// stable signal without dragging in a Temporal namespace.\n//\n// Per-type discriminators (verified against @js-temporal/polyfill's\n// prototype method list, which matches the spec):\n//\n// PlainDate — `toPlainDateTime` + `withCalendar` + year/month/day,\n// no hour. (PlainDateTime also has `toPlainDateTime`'s\n// sibling `toPlainDate`/`toPlainTime`, but carries\n// hour — distinguished by time-field presence.)\n// PlainTime — no `toPlainDateTime`, no `withCalendar`. Has hour,\n// no year/month/day. (The only Temporal date/time type\n// that has neither `withCalendar` nor any `toPlainX`\n// method.)\n// PlainDateTime — `toPlainDate` + `withPlainTime` + year/month/day\n// AND hour. (`withPlainTime` is unique — only\n// PlainDateTime and ZonedDateTime carry it; ZonedDateTime\n// also carries `toInstant`, which PlainDateTime doesn't.)\n// ZonedDateTime — `toInstant` + `withTimeZone`. (Both are unique —\n// only ZonedDateTime carries `withTimeZone`.)\n// Instant — `toZonedDateTimeISO`, no `toInstant`. (Instant IS\n// the instant; the `toInstant` method belongs to\n// ZonedDateTime. `toZonedDateTimeISO` exists on\n// Instant and ZonedDateTime per the spec, but on\n// ZonedDateTime it's not exposed via the prototype\n// in either native or polyfill — only Instant has it\n// as a method. Verified by probing both.)\n// PlainYearMonth — `toPlainDate` (takes a day arg) + year/month,\n// no day. (PlainDate also has `toPlainDate`-shaped\n// methods but carries day; PlainMonthDay also has\n// `toPlainDate` but carries day, not year.)\n// PlainMonthDay — `toPlainDate` + month/day, no year. Plus: lacks\n// `add`/`subtract`/`until`/`since` (calendar arithmetic\n// doesn't apply to a month-day without a year).\n// Duration — `total` + `abs` + `negated`. (All three are unique\n// to Duration; no other Temporal type carries any of them.)\n\n// The shapes below intentionally mirror TemporalLike in tokens.ts but\n// stay loose enough to also match real Temporal objects (which carry\n// the spec methods the field-only TemporalLike doesn't).\ninterface TemporalInstance {\n toInstant?: () => unknown;\n toPlainDate?: (arg?: unknown) => unknown;\n toPlainDateTime?: (arg?: unknown) => unknown;\n toPlainTime?: () => unknown;\n toPlainYearMonth?: () => unknown;\n toPlainMonthDay?: () => unknown;\n toZonedDateTime?: (arg: unknown) => unknown;\n toZonedDateTimeISO?: (arg: unknown) => unknown;\n withCalendar?: (cal: unknown) => unknown;\n withPlainTime?: (t: unknown) => unknown;\n withTimeZone?: (z: unknown) => unknown;\n getTimeZoneTransition?: (opts: unknown) => unknown;\n startOfDay?: () => unknown;\n sign?: () => number;\n blank?: () => boolean;\n total?: (opts: unknown) => number;\n abs?: () => unknown;\n negated?: () => unknown;\n toString?: () => string;\n // Fields from TemporalLike — present on all date-carrying Temporal\n // types but absent on PlainTime (which has no calendar position).\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n calendarId?: string;\n timeZoneId?: string;\n // PlainMonthDay stores its month as `monthCode` (e.g. \"M08\"), not\n // as a numeric `month` field — without a year there's no way to\n // disambiguate which month a calendar-independent numeric month\n // refers to in leap-year-aware calendars, so the spec uses\n // `monthCode` instead. PlainMonthDay is the only Temporal type that\n // exposes `monthCode` without also exposing `month`.\n monthCode?: string;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null;\n}\n\n// Hostile-getter hardening: the structural guards below read properties\n// (year, hour, Symbol.toStringTag, ...) off arbitrary caller values. An\n// object with a throwing getter used to turn isPlainDate(obj) — a function\n// whose whole contract is returning a boolean — into an exception factory.\n// Every exported guard now runs through this wrapper: a throw during\n// probing means \"not one of ours\", not a crash. One shared catch (rather\n// than one per guard) keeps the coverage burden honest too.\nfunction guardOrFalse<T>(impl: (value: unknown) => value is T): (value: unknown) => value is T {\n return (value: unknown): value is T => {\n try {\n return impl(value);\n } catch {\n return false;\n }\n };\n}\n\nfunction hasMethod<T extends keyof TemporalInstance>(\n v: TemporalInstance,\n name: T,\n): boolean {\n return typeof v[name] === 'function';\n}\n\n// Symbol.toStringTag is the spec-mandated brand for Temporal types —\n// every Temporal instance carries a tag like \"Temporal.PlainDate\" via\n// this well-known symbol, regardless of whether it's native or polyfill.\n// Used by the per-type guards as a fast positive signal: a tag match\n// is sufficient, the structural fallback (field presence + method checks)\n// is only consulted when the tag isn't set (which can happen with\n// hand-built mock objects or partial implementations).\nfunction hasTag(value: unknown, typeName: string): boolean {\n if (!isObject(value)) return false;\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag];\n return tag === `Temporal.${typeName}`;\n}\n\nexport const isPlainDate: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainDateImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainDate')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'undefined'\n && hasMethod(v, 'toPlainDateTime')\n && hasMethod(v, 'withCalendar');\n});\n\nexport const isPlainTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // PlainTime is the only Temporal type with `hour` but no `year` and\n // neither `withCalendar` nor any `toPlainX` method. That combination\n // is unique among the eight types.\n return typeof v.hour === 'number'\n && typeof v.year === 'undefined'\n && !hasMethod(v, 'withCalendar')\n && !hasMethod(v, 'toPlainDate')\n && !hasMethod(v, 'toPlainDateTime');\n});\n\nexport const isPlainDateTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainDateTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainDateTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // `withPlainTime` is on PlainDateTime and ZonedDateTime; ZonedDateTime\n // also carries `toInstant`. The combination \"has withPlainTime, no\n // toInstant\" uniquely identifies PlainDateTime.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'number'\n && hasMethod(v, 'withPlainTime')\n && !hasMethod(v, 'toInstant');\n});\n\nexport const isZonedDateTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isZonedDateTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'ZonedDateTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // `withTimeZone` is unique to ZonedDateTime across every Temporal type.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'number'\n && hasMethod(v, 'withTimeZone')\n && hasMethod(v, 'toInstant');\n});\n\nexport const isInstant: (value: unknown) => value is TemporalInstance = guardOrFalse(function isInstantImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'Instant')) return true;\n if (!isObject(value)) return false;\n // Instant carries `toZonedDateTimeISO` and no `toInstant` (it IS the\n // instant). It also has no year/month/day/hour fields — those are\n // only meaningful against a calendar and zone, which Instant alone\n // doesn't carry.\n const v = value as TemporalInstance;\n return hasMethod(v, 'toZonedDateTimeISO')\n && !hasMethod(v, 'toInstant')\n && typeof v.year === 'undefined';\n});\n\nexport const isPlainYearMonth: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainYearMonthImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainYearMonth')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // Has year+month, no day, carries `toPlainDate` (used to attach a day).\n // PlainMonthDay also carries `toPlainDate` but has no `year` field.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'undefined'\n && hasMethod(v, 'toPlainDate');\n});\n\nexport const isPlainMonthDay: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainMonthDayImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainMonthDay')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // PlainMonthDay has `day` and `monthCode` but no `year` and no numeric\n // `month`. The `monthCode` field is the discriminator — every other\n // Temporal type that has a month concept either exposes `month` (numeric,\n // calendar-aware) or doesn't expose months at all (PlainTime, Duration).\n return typeof v.year === 'undefined'\n && typeof v.month === 'undefined'\n && typeof v.day === 'number'\n && typeof v.monthCode === 'string'\n && hasMethod(v, 'toPlainDate');\n});\n\nexport const isDuration: (value: unknown) => value is Record<string, unknown> = guardOrFalse(function isDurationImpl(value: unknown): value is Record<string, unknown> {\n if (hasTag(value, 'Duration')) return true;\n if (!isObject(value)) return false;\n // `total` is unique to Duration across every Temporal type — no other\n // type carries it (verified by probing the prototype of each).\n // `abs` and `negated` are also Duration-only. Checking any one of\n // them is sufficient; checking `total` because it's the most semantically\n // distinctive (a Duration-specific operation no other type would\n // coincidentally need).\n const v = value as TemporalInstance;\n return hasMethod(v, 'total');\n});\n\n// Umbrella guard: any Temporal namespace member. Used by `assertTemporal`\n// below for the \"this needs to be some Temporal thing\" case. Two signals\n// checked in order:\n// 1. `Symbol.toStringTag` — every Temporal type brands itself as\n// `Temporal.X` via this well-known symbol. This is the spec-\n// mandated brand, present on native and polyfill instances alike.\n// 2. The discriminating-method fallback — covers any case where a\n// future/partial Temporal impl doesn't set the tag. Method\n// presence is part of the Temporal spec contract; combined with\n// `equals` + `with` (both present on every Temporal instance),\n// it's a reliable signal that doesn't rely on the toStringTag\n// being set.\nexport const isTemporal: (value: unknown) => value is TemporalInstance = guardOrFalse(function isTemporalImpl(value: unknown): value is TemporalInstance {\n if (!isObject(value)) return false;\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag];\n if (typeof tag === 'string' && tag.startsWith('Temporal.')) return true;\n const v = value as TemporalInstance;\n return hasMethod(v, 'toInstant')\n || hasMethod(v, 'toPlainDate')\n || hasMethod(v, 'toPlainDateTime')\n || hasMethod(v, 'toPlainTime')\n || hasMethod(v, 'toPlainYearMonth')\n || hasMethod(v, 'toPlainMonthDay')\n || hasMethod(v, 'toZonedDateTime')\n || hasMethod(v, 'toZonedDateTimeISO')\n || hasMethod(v, 'withCalendar')\n || hasMethod(v, 'withPlainTime')\n || hasMethod(v, 'withTimeZone')\n || hasMethod(v, 'total');\n});\n\n// Assertion helpers — throw descriptively rather than returning false.\n// Same convention as the rest of the library: descriptive thrown errors\n// instead of silent failures. The thrown Error carries a `valueType`\n// hint in the message so the caller can see what they actually passed.\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object') {\n const ctor = (value as { constructor?: { name?: string } }).constructor;\n if (ctor && typeof ctor.name === 'string' && ctor.name !== 'Object') {\n return `instance of ${ctor.name}`;\n }\n return 'plain object';\n }\n return typeof value;\n}\n\nfunction assertImpl(value: unknown, guard: (v: unknown) => boolean, expectedType: string): void {\n if (!guard(value)) {\n throw new Error(\n `temporal-fmt: expected a Temporal.${expectedType}, got ${describeValue(value)}.`\n );\n }\n}\n\nexport function assertTemporal(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isTemporal, 'object (Instant, PlainDate, PlainTime, PlainDateTime, ZonedDateTime, Duration, PlainYearMonth, or PlainMonthDay)');\n}\nexport function assertInstant(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isInstant, 'Instant');\n}\nexport function assertPlainDate(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainDate, 'PlainDate');\n}\nexport function assertPlainTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainTime, 'PlainTime');\n}\nexport function assertPlainDateTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainDateTime, 'PlainDateTime');\n}\nexport function assertZonedDateTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isZonedDateTime, 'ZonedDateTime');\n}\nexport function assertPlainYearMonth(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainYearMonth, 'PlainYearMonth');\n}\nexport function assertPlainMonthDay(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainMonthDay, 'PlainMonthDay');\n}\nexport function assertDuration(value: unknown): asserts value is Record<string, unknown> {\n assertImpl(value, isDuration, 'Duration');\n}","// Per-token metadata: the structured descriptor this module\n// asks for (meaning, format-capable?, parse-capable?, locale-sensitive?,\n// calendar-sensitive?, timezone-sensitive?, supported Temporal types,\n// round-trip-safe?). One table, kept in sync with TOKENS in tokens.ts\n// by tests (see test/tokenMetadata.test.js — every entry in TOKENS has\n// a metadata entry, and vice versa).\n//\n// This is the single source of truth that analyzeFormat() (section E)\n// and the ESLint plugin / codemod consume — never read the\n// TOKENS array directly for metadata; use TOKEN_METADATA here, or the\n// tokenInfo() / listTokens() wrappers in analyze.ts.\n\nimport type { TemporalLike } from './tokens.js';\n\nexport type TemporalType =\n | 'Instant'\n | 'PlainDate'\n | 'PlainTime'\n | 'PlainDateTime'\n | 'ZonedDateTime'\n | 'PlainYearMonth'\n | 'PlainMonthDay';\n\nexport interface TokenMetadata {\n // Short human-readable description of what the token represents.\n meaning: string;\n // True if format() accepts this token. Every token in TOKENS is\n // format-capable by construction; this flag exists so the table is\n // self-describing for callers (the codemod) that work with token\n // families including format-only tokens from other libraries.\n formatCapable: boolean;\n // True if parse() accepts this token. False for `do`, `ww`, `RRRR`\n // — see FORMAT_ONLY_TOKENS in pattern.ts.\n parseCapable: boolean;\n // True if the output depends on locale (resolves via Intl or\n // custom vocab). MMMM/MMM/EEEE/EEE/a are true; everything else is\n // numeric ASCII and locale-independent.\n localeSensitive: boolean;\n // True if the output depends on the calendar (gregory, hebrew,\n // etc.). Locale-aware tokens inherit calendar sensitivity from\n // the Intl path (a hebrew PlainDate's month name depends on the\n // hebrew calendar). Year/month/day numeric tokens are calendar-\n // sensitive in principle (a Hebrew year/month renders differently\n // than a Gregorian one) but stay ISO-numeric on the format side\n // because tokens.ts reads straight off TemporalLike fields, which\n // are already calendar-specific by the time format() sees them.\n calendarSensitive: boolean;\n // True if the token requires a timeZoneId or offset field — i.e.\n // only ZonedDateTime supports it. zzz and the six offset tokens\n // (X/XX/XXX/x/xx/xxx) are timezone-sensitive.\n timezoneSensitive: boolean;\n // Which Temporal types can supply the field this token needs.\n // PlainTime can't supply year; PlainDate can't supply hour; only\n // ZonedDateTime carries timeZoneId/offset.\n supportedTypes: TemporalType[];\n // True if format(x, fmt) → parse(fmt, output) round-trips back to\n // a value equal to x. False when the format is fundamentally lossy:\n // `do` (ordinal suffix isn't structurally parseable), `ww`/`RRRR`\n // (week + weekday alone can't reconstruct a date without an extra\n // disambiguator), `yy` (two-digit year loses century — strptime\n // heuristic resolves it, but the heuristic isn't symmetric with\n // how `yy` formats BCE years, so round-trip only works for years\n // where 00-68/69-99 lands back on the same century).\n roundTripSafe: boolean;\n}\n\n// Helper: which Temporal types carry each field. Centralized here so\n// the supportedTypes arrays below stay short and the table is easy to\n// audit. Mirrors the field checks in tokens.ts (handler third tuple\n// element).\nconst TYPES_WITH: Record<string, TemporalType[]> = {\n year: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainYearMonth'],\n month: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainYearMonth', 'PlainMonthDay'],\n day: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainMonthDay'],\n hour: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n minute: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n second: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n millisecond: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n dayOfWeek: ['PlainDate', 'PlainDateTime', 'ZonedDateTime'],\n timeZoneId: ['ZonedDateTime'],\n offset: ['ZonedDateTime'],\n};\n\n// Every token in TOKENS gets an entry here. Adding a token to TOKENS\n// without adding one here makes test/tokenMetadata.test.js fail.\nexport const TOKEN_METADATA: Record<string, TokenMetadata> = {\n yyyy: {\n meaning: 'Four-digit year (preserves sign for BCE; no truncation).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: true,\n },\n yy: {\n meaning: 'Two-digit year (year % 100). Throws on negative years.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n MMMM: {\n meaning: 'Long month name (locale-aware, e.g. \"January\", \"janvier\").',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n MMM: {\n meaning: 'Short month name (locale-aware, e.g. \"Jan\", \"janv.\").',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n MM: {\n meaning: 'Two-digit month (01-12, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n M: {\n meaning: 'One- or two-digit month (1-12, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n dd: {\n meaning: 'Two-digit day-of-month (01-31, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: true,\n },\n d: {\n meaning: 'One- or two-digit day-of-month (1-31, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: true,\n },\n EEEE: {\n meaning: 'Long weekday name (locale-aware, e.g. \"Monday\", \"lundi\"). Parse cross-checks against the parsed date.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n EEE: {\n meaning: 'Short weekday name (locale-aware, e.g. \"Mon\", \"lun.\"). Parse cross-checks against the parsed date.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n HH: {\n meaning: 'Two-digit hour, 24-hour clock (00-23, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n H: {\n meaning: 'One- or two-digit hour, 24-hour clock (0-23, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n hh: {\n meaning: 'Two-digit hour, 12-hour clock (01-12, zero-padded). Requires `a` token on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n h: {\n meaning: 'One- or two-digit hour, 12-hour clock (1-12, no padding). Requires `a` token on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n mm: {\n meaning: 'Two-digit minute (00-59, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.minute, roundTripSafe: true,\n },\n m: {\n meaning: 'One- or two-digit minute (0-59, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.minute, roundTripSafe: true,\n },\n ss: {\n meaning: 'Two-digit second (00-59, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.second, roundTripSafe: true,\n },\n s: {\n meaning: 'One- or two-digit second (0-59, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.second, roundTripSafe: true,\n },\n S: {\n meaning: 'Fractional second, 1 digit (tenths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SS: {\n meaning: 'Fractional second, 2 digits (hundredths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSS: {\n meaning: 'Fractional second, 3 digits (milliseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSS: {\n meaning: 'Fractional second, 4 digits (tens of microseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSS: {\n meaning: 'Fractional second, 5 digits (hundred-thousandths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSS: {\n meaning: 'Fractional second, 6 digits (microseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSS: {\n meaning: 'Fractional second, 7 digits (tens of nanoseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSSS: {\n meaning: 'Fractional second, 8 digits (hundred-millionths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSSSS: {\n meaning: 'Fractional second, 9 digits (nanoseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n a: {\n meaning: 'Day period (locale-aware: \"AM\"/\"PM\", \"午前\"/\"午後\", etc.). Required alongside 12-hour tokens on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n zzz: {\n meaning: 'IANA time zone id (e.g. \"America/New_York\") or fixed offset (\"+05:30\"). Requires full date+time on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: true,\n },\n X: {\n meaning: 'Numeric UTC offset, short (Z for UTC, ±HH[MM] otherwise). Minutes omitted when zero.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n XX: {\n meaning: 'Numeric UTC offset, no colon (Z for UTC, ±HHMM otherwise).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n XXX: {\n meaning: 'Numeric UTC offset, with colon (Z for UTC, ±HH:MM otherwise).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n x: {\n meaning: 'Numeric UTC offset, short, never Z (always ±HH[MM]). Lowercase variant of X.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n xx: {\n meaning: 'Numeric UTC offset, no colon, never Z (always ±HHMM). Lowercase variant of XX.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n xxx: {\n meaning: 'Numeric UTC offset, with colon, never Z (always ±HH:MM). Lowercase variant of XXX.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n do: {\n meaning: 'Ordinal day-of-month with English suffix (\"1st\", \"2nd\", \"3rd\", \"4th\", ... 21st, 22nd, ...). Format-only — not parseable.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n Q: {\n meaning: 'Quarter as a digit (1-4). Derived from month.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n QQQ: {\n meaning: 'Quarter with \"Q\" prefix (\"Q1\"..\"Q4\"). Derived from month.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n ww: {\n meaning: 'ISO 8601 week number (01-53). Format-only — week alone can\\'t reconstruct a date.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: false,\n },\n RRRR: {\n meaning: 'ISO 8601 week-numbering year (4-digit, may differ from calendar year at year boundaries). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: false,\n },\n D: {\n meaning: 'Day of year, unpadded (1-366). Format-only — parsing requires resolving against a year.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n DD: {\n meaning: 'Day of year, 2-digit minimum (01-366). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n DDD: {\n meaning: 'Day of year, 3-digit zero-padded (001-366). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n LLLL: {\n meaning: 'Stand-alone long month name (nominative case in Slavic locales). Identical to MMMM in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n LLL: {\n meaning: 'Stand-alone short month name. Identical to MMM in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n cccc: {\n meaning: 'Stand-alone long weekday name. Identical to EEEE in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n ccc: {\n meaning: 'Stand-alone short weekday name. Identical to EEE in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n GGGG: {\n meaning: 'Long era name (locale-aware, e.g. \"Anno Domini\"/\"Before Christ\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n G: {\n meaning: 'Short era name (locale-aware, e.g. \"AD\"/\"BC\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n zzzz: {\n meaning: 'Localized long timezone name (e.g. \"Eastern Standard Time\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: false,\n },\n z: {\n meaning: 'Localized short timezone name (e.g. \"EST\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: false,\n },\n\n};\n\nexport const ALL_TOKEN_NAMES: string[] = Object.keys(TOKEN_METADATA);","// Format-string analysis and introspection. Single source of truth for\n// \"what does this format string need / accept / produce\" — consumed by\n// the ESLint plugin and the codemod\n// so they don't each carry their own token table that drifts out of\n// sync with the runtime. Everything here reads from TOKENS in\n// tokens.ts and TOKEN_METADATA in tokenMetadata.ts, so adding a token\n// to those two tables automatically makes it visible to analyze()\n// without touching this file.\n\nimport { TOKENS, type TemporalLike } from './tokens.js';\nimport { TOKEN_METADATA, type TokenMetadata, type TemporalType } from './tokenMetadata.js';\nimport { tokenize, type Piece } from './tokenize.js';\nimport { _handlerFor } from './format.js';\nimport { UNPADDED_NUMERIC_TOKENS, FORMAT_ONLY_TOKENS } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\n// Re-exported so callers can import everything analyzer-related from a\n// single entry point — analyze.ts is the introspection surface.\nexport type { TokenMetadata, TemporalType } from './tokenMetadata.js';\nexport type { Piece } from './tokenize.js';\n\n// tokenizeFormat is the same tokenize() the runtime uses, exposed\n// under a name that signals \"this is the introspection entry point.\"\n// Same implementation, different name — callers reading analyze.ts\n// see a coherent API surface rather than having to know that the\n// runtime tokenizer is the right thing to call.\nexport function tokenizeFormat(formatStr: string): Piece[] {\n return tokenize(formatStr);\n}\n\n// listTokens: every token the runtime recognizes, with metadata. Used\n// by IDE autocomplete data (section AC) and the codemod's \"can this\n// source-library token be translated?\" lookup (section Z).\nexport function listTokens(): Array<{ name: string; metadata: TokenMetadata }> {\n return TOKENS.map(([name]) => ({ name, metadata: TOKEN_METADATA[name]! }));\n}\n\n// tokenInfo: one token's metadata. Returns undefined for unknown\n// tokens rather than throwing — callers (autocomplete, hover docs)\n// want a sentinel, not an exception, when a user types a partial token.\nexport function tokenInfo(name: string): TokenMetadata | undefined {\n return TOKEN_METADATA[name];\n}\n\nexport interface AnalyzedToken {\n name: string;\n // Position in the format string, 0-indexed. Useful for IDE diagnostics\n // that highlight a specific token.\n position: number;\n metadata: TokenMetadata;\n}\n\nexport interface FormatAnalysis {\n // Every token in the format string, in order, with metadata.\n tokens: AnalyzedToken[];\n // Fields (year/month/day/hour/...) any token in this format string\n // reads off the input. Lets a caller statically check \"does this\n // format string need a field PlainDate doesn't have?\"\n requiredFields: string[];\n // Temporal types that can supply every required field. The intersection\n // across all tokens — a format string with both `yyyy` (needs year)\n // and `HH` (needs hour) is compatible only with types carrying both,\n // i.e. PlainDateTime/ZonedDateTime.\n compatibleTypes: TemporalType[];\n // Can parse() accept this format string? False when any token is\n // format-only (do/ww/RRRR per FORMAT_ONLY_TOKENS in pattern.ts).\n parseable: boolean;\n // Does the format string contain any locale-aware token (MMMM/MMM/\n // EEEE/EEE/a)? True means the output depends on the locale option\n // and a registered custom vocab can override the result.\n localeSensitive: boolean;\n // Does the format string contain any calendar-sensitive token?\n // Almost every date/weekday token is, since its output depends on\n // the object's calendar (a hebrew PlainDate's \"MMMM\" is a hebrew\n // month name). Pure-time tokens (HH/mm/ss) are not.\n calendarSensitive: boolean;\n // Does the format string contain zzz or any offset token? If yes,\n // only ZonedDateTime can supply the field.\n timezoneSensitive: boolean;\n // Does the format string contain a run of 2+ adjacent unpadded\n // numeric tokens with no literal between them (Md, dM, Hms, ...)?\n // Such runs can be ambiguous at parse time — strict mode throws,\n // lenient mode picks one split via the documented heuristic.\n ambiguous: boolean;\n // Does format(x, fmt) → parse(fmt, output) round-trip? False when\n // any token is format-only OR when yy is used (century loss).\n roundTripSafe: boolean;\n // Non-fatal issues found during analysis. Each carries a `code`\n // the IDE plugin / CLI can switch on. Empty for clean format strings.\n warnings: Array<{ code: string; message: string }>;\n}\n\nexport function analyzeFormat(formatStr: string): FormatAnalysis {\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 const pieces = tokenize(formatStr);\n const tokens: AnalyzedToken[] = [];\n const requiredFields = new Set<string>();\n const compatibleTypesSet = new Set<TemporalType>();\n let parseable = true;\n let localeSensitive = false;\n let calendarSensitive = false;\n let timezoneSensitive = false;\n let roundTripSafe = true;\n const warnings: Array<{ code: string; message: string }> = [];\n\n // Walk pieces, building up the analysis. Position is computed per\n // token piece (literals count toward offset but don't appear in the\n // tokens array).\n let offset = 0;\n const unpaddedRun: string[] = [];\n const flushRun = () => {\n if (unpaddedRun.length >= 2) {\n warnings.push({\n code: 'AMBIGUOUS_NUMERIC_RUN',\n message: `Adjacent unpadded numeric tokens \"${unpaddedRun.join('')}\" with no separator can be ambiguous to parse. Add a separator or use the padded form (e.g. \"MM\" instead of \"M\").`,\n });\n }\n unpaddedRun.length = 0;\n };\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n offset += piece.value.length;\n flushRun();\n continue;\n }\n const metadata = TOKEN_METADATA[piece.value];\n // Dead by construction, confirmed via listTokens(): every token in\n // TOKENS has a matching TOKEN_METADATA entry (51/51), and tokenize()\n // already throws on any string that isn't in TOKENS before this loop\n // ever runs. Kept as a guard against the two tables drifting apart.\n /* c8 ignore start */\n if (!metadata) {\n warnings.push({\n code: 'UNKNOWN_TOKEN_NO_METADATA',\n message: `Token \"${piece.value}\" is recognized by the tokenizer but has no metadata entry in tokenMetadata.ts — this is a bug in temporal-fmt.`,\n });\n offset += piece.value.length;\n continue;\n }\n /* c8 ignore stop */\n tokens.push({ name: piece.value, position: offset, metadata });\n\n // Required fields: read off the handler map (the source of truth\n // for \"what field does this token need\"). Falls back to the\n // metadata's supportedTypes-derived field set if the handler isn't\n // found, which also shouldn't happen.\n const handler = _handlerFor(piece.value);\n if (handler) {\n requiredFields.add(handler.field as string);\n }\n\n // compatibleTypes: intersect every token's supportedTypes. The\n // first token seeds the set; subsequent tokens narrow it.\n if (tokens.length === 1) {\n for (const t of metadata.supportedTypes) compatibleTypesSet.add(t);\n } else {\n const stillCompatible = new Set<TemporalType>();\n for (const t of compatibleTypesSet) {\n if (metadata.supportedTypes.includes(t)) stillCompatible.add(t);\n }\n compatibleTypesSet.clear();\n for (const t of stillCompatible) compatibleTypesSet.add(t);\n }\n\n if (!metadata.parseCapable) {\n parseable = false;\n warnings.push({\n code: 'FORMAT_ONLY_TOKEN',\n message: `Token \"${piece.value}\" is format-only — it can't be parsed back into a value. Use a parse-capable variant (e.g. \"d\" instead of \"do\") if you need round-trip.`,\n });\n }\n if (metadata.localeSensitive) localeSensitive = true;\n if (metadata.calendarSensitive) calendarSensitive = true;\n if (metadata.timezoneSensitive) timezoneSensitive = true;\n if (!metadata.roundTripSafe) roundTripSafe = false;\n\n // Track runs of adjacent unpadded numeric tokens — same definition\n // as parsePattern.ts uses (UNPADDED_NUMERIC_TOKENS). A literal\n // between tokens breaks the run; this is checked by the flushRun\n // call above.\n if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n unpaddedRun.push(piece.value);\n } else {\n flushRun();\n }\n\n offset += piece.value.length;\n }\n flushRun();\n\n // Cross-token checks the runtime parse() performs. Mirroring them\n // here lets the ESLint plugin flag them statically without having\n // to re-derive the logic.\n const tokenNames = tokens.map((t) => t.name);\n const has12Hour = tokenNames.some((t) => t === 'hh' || t === 'h');\n const has24Hour = tokenNames.some((t) => t === 'HH' || t === 'H');\n const hasAPeriod = tokenNames.some((t) => t === 'a');\n if (has12Hour && !hasAPeriod) {\n warnings.push({\n code: 'TWELVE_HOUR_WITHOUT_A',\n message: '12-hour token (\"hh\"/\"h\") used without an \"a\" (AM/PM) token — parse() can\\'t tell AM from PM and throws at runtime.',\n });\n }\n if (has12Hour && has24Hour) {\n warnings.push({\n code: 'MIXED_12_AND_24_HOUR',\n message: 'Mixing 24-hour and 12-hour tokens — parse() refuses to guess which is authoritative and throws at runtime.',\n });\n }\n\n const hasOffset = tokenNames.some((t) => t === 'X' || t === 'XX' || t === 'XXX' || t === 'x' || t === 'xx' || t === 'xxx');\n const hasZzz = tokenNames.some((t) => t === 'zzz');\n if (hasOffset && hasZzz) {\n warnings.push({\n code: 'ZZZ_WITH_OFFSET_TOKEN',\n message: 'Format string has both \"zzz\" and an offset token — parse() cross-checks them and throws if the parsed offset disagrees with the zone\\'s actual offset.',\n });\n }\n if (hasOffset && !requiredFields.has('year')) {\n warnings.push({\n code: 'OFFSET_WITHOUT_FULL_DATE',\n message: 'Offset token used without a full date — parse() needs a complete date and time to build a ZonedDateTime and throws at runtime otherwise.',\n });\n }\n\n return {\n tokens,\n requiredFields: [...requiredFields].sort(),\n compatibleTypes: [...compatibleTypesSet].sort(),\n parseable,\n localeSensitive,\n calendarSensitive,\n timezoneSensitive,\n // Ambiguous iff at least one warning fired about an ambiguous run.\n ambiguous: warnings.some((w) => w.code === 'AMBIGUOUS_NUMERIC_RUN'),\n roundTripSafe: roundTripSafe && parseable,\n warnings,\n };\n}\n\n// explainFormat: human-readable rendering of analyzeFormat's output.\n// Used by the CLI (section AD) for `temporal-fmt inspect <fmt>` and by\n// the IDE for hover docs on a format string. Plain prose, no JSON —\n// callers wanting machine-readable output should call analyzeFormat()\n// directly.\nexport function explainFormat(formatStr: string): string {\n const analysis = analyzeFormat(formatStr);\n const lines: string[] = [];\n lines.push(`Format string: \"${formatStr}\"`);\n lines.push(`Tokens (${analysis.tokens.length}):`);\n for (const t of analysis.tokens) {\n lines.push(` ${t.name} @${t.position} — ${t.metadata.meaning}`);\n }\n if (analysis.requiredFields.length > 0) {\n lines.push(`Required fields: ${analysis.requiredFields.join(', ')}`);\n }\n if (analysis.compatibleTypes.length > 0) {\n lines.push(`Compatible Temporal types: ${analysis.compatibleTypes.join(', ')}`);\n }\n lines.push(`Parseable: ${analysis.parseable ? 'yes' : 'no'}`);\n lines.push(`Locale-sensitive: ${analysis.localeSensitive ? 'yes' : 'no'}`);\n lines.push(`Calendar-sensitive: ${analysis.calendarSensitive ? 'yes' : 'no'}`);\n lines.push(`Timezone-sensitive: ${analysis.timezoneSensitive ? 'yes' : 'no'}`);\n lines.push(`Ambiguous: ${analysis.ambiguous ? 'yes' : 'no'}`);\n lines.push(`Round-trip safe: ${analysis.roundTripSafe ? 'yes' : 'no'}`);\n if (analysis.warnings.length > 0) {\n lines.push('Warnings:');\n for (const w of analysis.warnings) {\n lines.push(` [${w.code}] ${w.message}`);\n }\n }\n return lines.join('\\n');\n}\n\n// isValidFormat: true iff tokenize() accepts the string without\n// throwing. Distinct from analyzeFormat's `parseable` flag — a format\n// string can be valid (tokenizes cleanly) but not parseable (contains\n// a format-only token like `do`). Both checks are exposed since callers\n// want different things: validity for \"will format() throw\", parseability\n// for \"will parse() throw\".\nexport function isValidFormat(formatStr: string): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) return false;\n try {\n tokenize(formatStr);\n return true;\n } catch {\n return false;\n }\n}\n\n// validateFormat: throws on invalid format strings instead of\n// returning a boolean. Mirrors the existing strict-validation\n// convention (descriptive thrown errors, never silent failures).\n// Returns the analysis for callers who want both — validate-then-use.\nexport function validateFormat(formatStr: string): FormatAnalysis {\n const analysis = analyzeFormat(formatStr);\n return analysis;\n}\n\n// Exported for callers that want to query the token table without\n// touching internals. Kept here rather than re-exported from\n// tokenMetadata.ts so this module is the single import for \"anything\n// analyzer-related.\"\nexport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\n\n// Re-export FORMAT_ONLY_TOKENS so callers don't have to import from\n// pattern.ts (which is a lower-level module they shouldn't need to\n// know about).\nexport { FORMAT_ONLY_TOKENS } from './pattern.js';\n\n// Used by callers that need to look up which field a token requires —\n// e.g. the ESLint plugin's \"type mismatch\" check (section Y) wants to\n// answer \"does the type the caller passed to format() carry this\n// field?\" without having to actually call format() and catch the\n// thrown error. Mirrors the same lookup format.ts does internally.\nexport function fieldForToken(token: string): keyof TemporalLike | undefined {\n return _handlerFor(token)?.field;\n}","// Calendar utility helpers. These are pure functions\n// over the TemporalLike shape — no Temporal namespace needed, same\n// approach as isoWeek.ts and the field-reading helpers in format.ts.\n// Letting callers compute dayOfYear/weekOfYear/etc. without going\n// through format() means they can build their own derived values\n// without committing to a string format.\n//\n// Calendar-sensitivity: the helpers in this module assume the\n// iso8601 (Gregorian) calendar — that's what TemporalLike fields\n// carry for the overwhelming majority of callers. Non-Gregorian\n// calendars (hebrew, islamic, etc.) need their own helpers; this\n// module doesn't try to be calendar-polymorphic the way Temporal\n// itself is. Documented limitation, not a design choice — see\n// VERIFICATION.md for the rationale.\n\nimport { isGregorianLeapYear, dayOfYear, isoWeekYearAndWeek } from './isoWeek.js';\n\n// A subset of TemporalLike that has the date fields these helpers need,\n// plus optional time fields. PlainTime isn't a DateFieldView (no\n// year/month/day), but PlainDateTime / ZonedDateTime / PlainDate all\n// match. Time fields are optional so callers can pass a PlainDate\n// to startOf(value, 'month') without having to populate hour/minute/etc.\n// Exported so the comparison/arithmetic modules can use the same\n// narrowing.\nexport interface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n dayOfWeek?: number;\n calendarId?: string;\n}\n\nfunction requireFields(view: DateFieldView, fields: Array<keyof DateFieldView>): void {\n for (const f of fields) {\n if (typeof view[f] !== 'number') {\n throw new Error(\n `temporal-fmt: calendar helper requires \"${String(f)}\", which this value doesn't have. ` +\n `Pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n }\n}\n\nexport function daysInMonth(view: DateFieldView): number {\n requireFields(view, ['year', 'month']);\n const { year, month } = view;\n // Standard Gregorian month lengths. February's length depends on\n // whether `year` is a leap year — the same isGregorianLeapYear check\n // isoWeek.ts uses for dayOfYear arithmetic.\n const LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n if (month === 2 && isGregorianLeapYear(year!)) return 29;\n return LENGTHS[(month! - 1)!]!;\n}\n\nexport function daysInYear(view: DateFieldView): 365 | 366 {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!) ? 366 : 365;\n}\n\nexport function monthsInYear(_view: DateFieldView): 12 {\n // Gregorian always has 12 months. Other calendars (Hebrew leap years\n // have 13) need calendar-aware logic this module doesn't carry — see\n // the file-level comment. The `_view` parameter is kept so the\n // signature mirrors the other helpers and a future calendar-aware\n // implementation can use it without changing call sites.\n return 12;\n}\n\nexport function isLeapYear(view: DateFieldView): boolean {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!);\n}\n\n// `isLeapMonth` would require knowing which month of a leap-year-aware\n// calendar is the leap month — Gregorian doesn't have one, so this\n// returns false unconditionally. Kept here so the public surface\n// matches the module's listing; non-Gregorian calendars need\n// a different implementation.\nexport function isLeapMonth(_view: DateFieldView): boolean {\n return false;\n}\n\nexport function dayOfYearHelper(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day']);\n return dayOfYear(view.year!, view.month!, view.day!);\n}\n\n// ISO 8601 week and week-year. Delegates to isoWeek.ts's\n// isoWeekYearAndWeek, which does the full Thursday-of-week\n// computation to handle the year-boundary cases (Dec 29-31 belonging\n// to week 1 of next year, Jan 1-3 belonging to week 52/53 of the\n// previous year).\nexport function weekOfYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).week;\n}\n\nexport function weekYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).isoYear;\n}\n\n/**\n * Fiscal-quarter options. `startMonth` is the calendar month (1-12) the\n * fiscal year begins on — e.g. `7` for a fiscal year starting in July.\n * Omitted or `1` gives the calendar-quarter behavior getQuarter() has\n * always had (Jan-Mar = Q1, etc.), so existing callers passing nothing\n * see no change.\n */\nexport interface QuarterOptions {\n startMonth?: number;\n}\n\nfunction validateStartMonth(startMonth: number): void {\n if (!Number.isInteger(startMonth) || startMonth < 1 || startMonth > 12) {\n throw new Error(\n `temporal-fmt: getQuarter's startMonth must be an integer from 1 to 12 (got ${startMonth}).`\n );\n }\n}\n\nexport function getQuarter(view: DateFieldView, options: QuarterOptions = {}): number {\n requireFields(view, ['month']);\n const startMonth = options.startMonth ?? 1;\n validateStartMonth(startMonth);\n if (startMonth === 1) {\n // Mirrors the Q token: months 1-3 → Q1, 4-6 → Q2, 7-9 → Q3, 10-12 → Q4.\n return Math.ceil(view.month! / 3);\n }\n // Fiscal case: shift the month so startMonth becomes month 1 of the\n // fiscal year (mod 12, 1-indexed), then apply the same ceil(/3) rule.\n // E.g. startMonth=7 (fiscal year starts July): July→1, Aug→2, ...,\n // Dec→6, Jan→7, ..., June→12. Then Q1 = fiscal months 1-3 (Jul-Sep),\n // matching the common \"FY starts in July\" convention where Q1 is the\n // first quarter of the fiscal year, not a quarter numbered by which\n // calendar quarter it falls in.\n const shifted = ((view.month! - startMonth + 12) % 12) + 1;\n return Math.ceil(shifted / 3);\n}\n\n// `getMonth` / `getWeekday` look trivial (just read the field) but\n// they're kept as real functions for API surface consistency with the\n// rest of this module. They also normalize: getWeekday returns 1-7\n// (Mon-Sun, matching Temporal's spec) regardless of what numbering the\n// caller's underlying value uses.\nexport function getMonth(view: DateFieldView): number {\n requireFields(view, ['month']);\n return view.month!;\n}\n\nexport function getWeekday(view: DateFieldView): number {\n requireFields(view, ['dayOfWeek']);\n return view.dayOfWeek!;\n}\n\n// startOf / endOf return new field bags (not Temporal objects — this\n// module is polyfill-free) with the relevant fields zeroed/extended.\n// Callers can pass the result to a Temporal constructor if they want\n// a typed value.\nexport type StartOfUnit = 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second';\n\n// Returns true if the given unit (when used with startOf/endOf) should\n// also touch the time fields. 'day', 'month', 'year' all imply a\n// resolution coarser than an hour, so startOf zeroes the time fields\n// and endOf maxes them. Sub-hour units (hour/minute/second) only touch\n// the fields finer than themselves.\nfunction touchesTime(unit: StartOfUnit): boolean {\n return unit === 'day' || unit === 'month' || unit === 'year';\n}\n\n// startOf/endOf reassign year/month/day, which invalidates any\n// dayOfWeek carried over from the input — a plain { ...view } spread\n// leaves the old value sitting there unchanged. Same failure mode\n// businessCalendar.ts's isBusinessDay() works around for add(); we\n// recompute here rather than trust the copied field.\nfunction recomputeDayOfWeek(view: DateFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: startOf()/endOf() both call\n * asDateFieldView() before this, which already throws if year/month/day\n * aren't all numbers — so by the time a value with a numeric dayOfWeek\n * reaches here, year/month/day are guaranteed present too. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\nexport function startOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 1;\n result.day = 1;\n } else if (unit === 'month') {\n result.day = 1;\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 0;\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'hour') {\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'minute') {\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'second') {\n result.millisecond = 0;\n }\n return result;\n}\n\nexport function endOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 12;\n result.day = daysInMonth({ year: result.year!, month: 12 });\n } else if (unit === 'month') {\n result.day = daysInMonth({ year: result.year!, month: result.month! });\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 23;\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'hour') {\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'minute') {\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'second') {\n result.millisecond = 999;\n }\n return result;\n}\n\n// Type-narrowing helpers used by the comparison/arithmetic modules.\n// Lets them accept any of the four date-carrying Temporal types without\n// importing Temporal itself.\nexport function asDateFieldView(value: unknown): DateFieldView {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: expected a date-carrying Temporal value, got ${String(value)}.`);\n }\n // Temporal instances expose year/month/day/etc. as prototype getters,\n // not own enumerable properties — so `{ ...value }` would lose them.\n // Read them explicitly. Only the fields actually present on this\n // value type end up in the returned view.\n const v = value as Record<string, unknown>;\n const out: DateFieldView = {};\n if (typeof v.year === 'number') out.year = v.year;\n if (typeof v.month === 'number') out.month = v.month;\n if (typeof v.day === 'number') out.day = v.day;\n if (typeof v.hour === 'number') out.hour = v.hour;\n if (typeof v.minute === 'number') out.minute = v.minute;\n if (typeof v.second === 'number') out.second = v.second;\n if (typeof v.millisecond === 'number') out.millisecond = v.millisecond;\n if (typeof v.dayOfWeek === 'number') out.dayOfWeek = v.dayOfWeek;\n if (typeof v.calendarId === 'string') out.calendarId = v.calendarId;\n if (out.year === undefined || out.month === undefined || out.day === undefined) {\n throw new Error(\n `temporal-fmt: value is missing year/month/day fields — pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return out;\n}\n\n// Re-export the TemporalType alias so callers can import everything\n// from one place.\nexport type { TemporalType } from './tokenMetadata.js';\n// Re-export TemporalLike for the same reason.\nexport type { TemporalLike } from './tokens.js';","// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');","// Comparison helpers. Pure functions over the field\n// shape, no Temporal namespace needed — same convention as\n// calendarUtils.ts. All comparisons are field-based, not identity-based,\n// so a polyfill PlainDate compares equal to a native PlainDate with the\n// same year/month/day.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction toComparableMs(view: DateTimeFieldView): number {\n // Exact proleptic-Gregorian day count (Howard Hinnant's days_from_civil,\n // same algorithm arithmetic.ts/interval.ts use) plus the time-of-day —\n // NOT an approximation. The previous implementation multiplied the\n // year offset by an average 365.2425 days/year, on the theory that the\n // error \"is constant for any given year, so it cancels out in a diff\".\n // That's only true when both dates fall in the same year: the year\n // term's error differs by up to ~0.76 days between adjacent years\n // (365.2425 vs 365/366 actual), which corrupts cross-year comparisons\n // near the boundary — e.g. isAfter(2025-01-01T00:00, 2024-12-31T18:12)\n // returned false (truth: ~5.8h after), and isEqual() returned true for\n // instants ~18h apart (2025-01-01T00:00 vs 2024-12-31T05:49:12).\n // Exact arithmetic has no such error term.\n const y = view.year!, m = view.month!, d = view.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n return days * 86_400_000\n + (view.hour ?? 0) * MS_PER_HOUR\n + (view.minute ?? 0) * MS_PER_MINUTE\n + (view.second ?? 0) * MS_PER_SECOND\n + (view.millisecond ?? 0);\n}\n\nfunction asDateTimeFieldView(value: unknown): DateTimeFieldView {\n const v = asDateFieldView(value);\n return v as DateTimeFieldView;\n}\n\n// Returns < 0 if a < b, 0 if a === b, > 0 if a > b. Same convention as\n// Array.prototype.sort and Temporal.PlainDate.compare. Lets callers\n// pass this directly as a comparator.\nexport function compare(a: unknown, b: unknown): number {\n const av = asDateTimeFieldView(a);\n const bv = asDateTimeFieldView(b);\n const am = toComparableMs(av);\n const bm = toComparableMs(bv);\n if (am < bm) return -1;\n if (am > bm) return 1;\n return 0;\n}\n\nexport function isEqual(a: unknown, b: unknown): boolean {\n return compare(a, b) === 0;\n}\n\nexport function isBefore(a: unknown, b: unknown): boolean {\n return compare(a, b) < 0;\n}\n\nexport function isAfter(a: unknown, b: unknown): boolean {\n return compare(a, b) > 0;\n}\n\nexport function min(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: min() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) < 0) best = values[i];\n }\n return best;\n}\n\nexport function max(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: max() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) > 0) best = values[i];\n }\n return best;\n}\n\nexport function clamp(value: unknown, lo: unknown, hi: unknown): unknown {\n if (compare(value, lo) < 0) return lo;\n if (compare(value, hi) > 0) return hi;\n return value;\n}\n\nexport function isBetween(value: unknown, lo: unknown, hi: unknown): boolean {\n return compare(value, lo) >= 0 && compare(value, hi) <= 0;\n}\n\n// Semantic helpers — same-day/week/month/quarter/year tests. Useful for\n// UI (\"is this event today?\") and for grouping/aggregation. All take a\n// reference value to compare against; the \"isToday\" / \"isTomorrow\" /\n// \"isYesterday\" variants compute the reference from the current system\n// date themselves so callers don't have to.\nfunction readYearMonthDay(value: unknown): { year: number; month: number; day: number } {\n const v = asDateFieldView(value);\n return { year: v.year!, month: v.month!, day: v.day! };\n}\n\nfunction todayYearMonthDay(): { year: number; month: number; day: number } {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n}\n\nfunction dateEqualYearMonthDay(a: { year: number; month: number; day: number }, b: { year: number; month: number; day: number }): boolean {\n return a.year === b.year && a.month === b.month && a.day === b.day;\n}\n\nexport function isSameDay(value: unknown, reference: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));\n}\n\nexport function isToday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());\n}\n\nfunction tomorrowYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() + 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nfunction yesterdayYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() - 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nexport function isTomorrow(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());\n}\n\nexport function isYesterday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());\n}\n\nfunction sameWeek(yearA: number, monthA: number, dayA: number, yearB: number, monthB: number, dayB: number): boolean {\n // Same week iff the dates are within 7 days of each other AND the earlier\n // one's weekday is ≤ the later one's weekday (so we don't say \"Sun and\n // the following Mon are in the same week\" — ISO weeks run Mon-Sun).\n // Easier: compute ISO week numbers via isoWeekYearAndWeek, compare both\n // year and week. That handles the year-boundary case (Dec 31 vs Jan 1 of\n // the next year, which can be in the same ISO week).\n // Local import to avoid module-cycle: isoWeek.ts doesn't import from here.\n // Lazy require-style dynamic via inline static import at top would be\n // cleaner; this inline computation is fine for a pure helper.\n // ...actually let's just compute weekday offsets directly. Same-ISO-week\n // iff |d1 - d2| < 7 days AND the earlier date's ISO weekday ≤ the later\n // date's ISO weekday so they fall in the same Mon-Sun span.\n const DAYS_PER_400_YEARS = 146097;\n const daysSince = (y: number, m: number, d: number): number => {\n // Zeller's congruence variant — convert Y/M/D to a day count.\n // Using a simple algorithm here that's correct for proleptic Gregorian.\n const y2 = m <= 2 ? y - 1 : y;\n const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((Math.floor(153 * m2 + 2) / 5) + d - 1);\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * DAYS_PER_400_YEARS + doe - 719468;\n };\n const a = daysSince(yearA, monthA, dayA);\n const b = daysSince(yearB, monthB, dayB);\n if (Math.abs(a - b) >= 7) return false;\n // Same week iff both dates round to the same Monday. Compute the\n // Monday-of-week for each: subtract (weekday - 1) days, where weekday\n // is 1=Mon..7=Sun. Use the standard Zeller formula for weekday.\n const weekdayOf = (y: number, m: number, d: number): number => {\n const days = daysSince(y, m, d);\n // 1970-01-01 was a Thursday (ISO 4). So weekday = ((days + 3) mod 7) + 1\n // mapped to Mon=1..Sun=7. Let's compute: Sun=0..Sat=6 via JS Date.\n const date = new Date(Date.UTC(y, m - 1, d));\n const jsDow = date.getUTCDay(); // 0=Sun..6=Sat\n return jsDow === 0 ? 7 : jsDow;\n };\n const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);\n const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);\n return wa === wb;\n}\n\nexport function isSameWeek(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);\n}\n\nexport function isSameMonth(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && a.month === b.month;\n}\n\nexport function isSameQuarter(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);\n}\n\nexport function isSameYear(value: unknown, reference: unknown): boolean {\n return readYearMonthDay(value).year === readYearMonthDay(reference).year;\n}\n\n// isWeekend / isWeekday depend on dayOfWeek — present on PlainDate,\n// PlainDateTime, ZonedDateTime, but NOT on PlainTime. Callers passing\n// PlainTime get a descriptive throw. Doesn't call asDateFieldView\n// because that would reject PlainTime (no year/month/day) before\n// reaching the dayOfWeek check — and the error we want here is the\n// \"no dayOfWeek field\" one, not the \"no date fields\" one.\nexport function isWeekend(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);\n }\n const v = value as { dayOfWeek?: unknown };\n if (typeof v.dayOfWeek !== 'number') {\n throw new Error(\n `temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`\n );\n }\n // 6=Sat, 7=Sun in Temporal's Mon=1..Sun=7 numbering.\n return v.dayOfWeek === 6 || v.dayOfWeek === 7;\n}\n\nexport function isWeekday(value: unknown): boolean {\n return !isWeekend(value);\n}\n","// Rounding helpers. Pure functions over field bags,\n// same convention as arithmetic.ts/calendarUtils.ts.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\nimport { InvalidDurationError } from './errors.js';\n\ntype RoundingUnit = 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\ntype RoundingMode = 'nearest' | 'floor' | 'ceil' | 'trunc';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\n// Ms-per-unit table. Used by round() to convert a field bag to ms,\n// apply the rounding mode, and convert back. Mirrors how add() in\n// arithmetic.ts handles sub-day arithmetic.\nconst MS_PER_UNIT: Record<RoundingUnit, number> = {\n day: 86_400_000,\n hour: 3_600_000,\n minute: 60_000,\n second: 1_000,\n millisecond: 1,\n};\n\nfunction applyMode(ms: number, mode: RoundingMode, increment: number): number {\n // All four modes operate on absolute value then re-apply the sign,\n // so negative values round symmetrically (floor of -1.5 → -2, not -1).\n // Matches Math.round semantics for 'nearest' and Temporal's rounding\n // for the other three.\n const sign = ms < 0 ? -1 : 1;\n const abs = Math.abs(ms);\n const stepped = abs / increment;\n let rounded: number;\n switch (mode) {\n case 'nearest': rounded = Math.round(stepped); break;\n case 'floor': rounded = Math.floor(stepped); break;\n case 'ceil': rounded = Math.ceil(stepped); break;\n case 'trunc': rounded = Math.trunc(stepped); break;\n }\n return sign * rounded * increment;\n}\n\nfunction toMs(v: DateTimeFieldView): number {\n // Returns ms-since-(Howard Hinnant epoch). Used internally — the\n // absolute value is meaningful only relative to the same epoch used\n // by fromMs.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n return days * MS_PER_UNIT.day\n + (v.hour ?? 0) * MS_PER_UNIT.hour\n + (v.minute ?? 0) * MS_PER_UNIT.minute\n + (v.second ?? 0) * MS_PER_UNIT.second\n + (v.millisecond ?? 0);\n}\n\nfunction fromMs(ms: number, base: DateTimeFieldView): DateTimeFieldView {\n // Convert absolute ms (on the same epoch as toMs) back to a field bag.\n // Walks the Howard Hinnant days_from_civil inverse inline.\n const MS_PER_DAY = MS_PER_UNIT.day;\n const totalDays = Math.floor(ms / MS_PER_DAY);\n let withinDay = ms - totalDays * MS_PER_DAY; // ms since midnight\n // Defensive floating-point guard: given totalDays = Math.floor(ms /\n // MS_PER_DAY), withinDay = ms - totalDays * MS_PER_DAY is\n // mathematically guaranteed non-negative (that's what Math.floor\n // division gives you), verified against extreme values including\n // Number.MIN_SAFE_INTEGER and sub-ms fractional noise near day\n // boundaries. Kept in case a future change to how ms is computed\n // upstream breaks that guarantee.\n /* c8 ignore next */\n if (withinDay < 0) withinDay += MS_PER_DAY;\n const hour = Math.floor(withinDay / MS_PER_UNIT.hour);\n const minute = Math.floor((withinDay % MS_PER_UNIT.hour) / MS_PER_UNIT.minute);\n const second = Math.floor((withinDay % MS_PER_UNIT.minute) / MS_PER_UNIT.second);\n const millisecond = withinDay % MS_PER_UNIT.second;\n // Convert totalDays back to year/month/day via Howard Hinnant's\n // civil_from_days. Same algorithm as arithmetic.ts's shiftDays.\n const z = totalDays + 719468;\n const era = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe = z - era * 146097;\n const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);\n const y2 = yoe + era * 400;\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));\n const mp = Math.floor((5 * doy + 2) / 153);\n const d = doy - Math.floor((153 * mp + 2) / 5) + 1;\n const m = mp < 10 ? mp + 3 : mp - 9;\n const year = m <= 2 ? y2 + 1 : y2;\n return { ...base, year, month: m, day: d, hour, minute, second, millisecond };\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n return { ...v, year: yOut, month: m2out, day: d2 };\n}\n\nexport interface RoundOptions {\n unit: RoundingUnit;\n mode?: RoundingMode;\n roundingIncrement?: number;\n}\n\nexport function round(value: unknown, options: RoundOptions): DateTimeFieldView {\n const base = asDateFieldView(value) as DateTimeFieldView;\n const mode = options.mode ?? 'nearest';\n const increment = (options.roundingIncrement ?? 1) * MS_PER_UNIT[options.unit];\n if (increment <= 0) {\n throw new Error(`temporal-fmt: round() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);\n }\n const ms = toMs(base);\n const rounded = applyMode(ms, mode, increment);\n return fromMs(rounded, base);\n}\n\nexport function floor(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'floor', roundingIncrement });\n}\n\nexport function ceil(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'ceil', roundingIncrement });\n}\n\nexport function truncate(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'trunc', roundingIncrement });\n}\n\n// Duration rounding. Takes a duration field bag and rounds it to the\n// requested unit. Operates on the duration's own fields (years/months\n// for calendar-bound units, days/hours/minutes/seconds/ms/µs/ns for\n// absolute units). Mirrors Temporal.Duration.prototype.round's surface\n// — but operates on plain field bags rather than real Temporal.Duration\n// instances, same convention as the rest of this module set.\nexport interface DurationFields {\n years?: number;\n months?: number;\n weeks?: number;\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n microseconds?: number;\n nanoseconds?: number;\n}\n\n// Converts one absolute-unit field to nanoseconds, exactly. Integer\n// values keep the exact BigInt path (arbitrary magnitude). Fractional\n// values — which parseISODuration legitimately produces (\"P1.5D\") —\n// scale in floating point and are accepted only when the product is a\n// safe integer, so P1.5D balances to exactly 1 day + 12 hours. A\n// fractional value whose product can't be represented exactly (large\n// fractional day counts) throws the typed InvalidDurationError instead\n// of the opaque \"TypeError: Cannot convert 1.5 to a BigInt\" the old\n// BigInt(v) call produced. Shared by roundDuration (here) and the\n// duration.ts arithmetic helpers — duration.ts already imports from\n// this module, so exporting keeps the dependency direction acyclic.\nexport function fieldToNs(v: number, nsPer: bigint, field: string): bigint {\n if (Number.isInteger(v)) return BigInt(v) * nsPer;\n const scaled = v * Number(nsPer);\n if (Number.isSafeInteger(scaled)) return BigInt(scaled);\n throw new InvalidDurationError({\n reason: `field \"${field}\" (${v}) cannot be converted to nanoseconds exactly — the value is too large for a fractional unit; use smaller units`,\n });\n}\n\ntype DurationUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' | 'microseconds' | 'nanoseconds';\n\nconst DURATION_UNIT_TO_NS: Record<DurationUnit, bigint> = {\n years: 0n, // calendar-bound, can't convert without relativeTo\n months: 0n, // same\n weeks: 0n, // same\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n};\n\n// Absolute units (days and below) can be rounded without a relativeTo\n// because their length is fixed in nanoseconds. Calendar-bound units\n// (years/months/weeks) can't — would need a Temporal.Duration round()\n// call with a relativeTo. This helper throws if the caller tries to\n// round to/from a calendar-bound unit, rather than silently producing\n// a wrong result.\nfunction isCalendarBound(unit: DurationUnit): boolean {\n return unit === 'years' || unit === 'months' || unit === 'weeks';\n}\n\nexport function roundDuration(duration: DurationFields, options: {\n unit: DurationUnit;\n mode?: RoundingMode;\n roundingIncrement?: number;\n}): DurationFields {\n const mode = options.mode ?? 'nearest';\n const increment = BigInt(options.roundingIncrement ?? 1);\n if (increment <= 0n) {\n throw new Error(`temporal-fmt: roundDuration() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);\n }\n if (isCalendarBound(options.unit)) {\n throw new Error(\n `temporal-fmt: roundDuration() to \"${options.unit}\" requires a Temporal.Duration with a relativeTo — ` +\n `this helper operates on plain field bags without calendar context. Use Temporal.Duration.prototype.round() directly.`\n );\n }\n\n // Sum all absolute-unit contributions into total nanoseconds.\n let totalNs = 0n;\n for (const u of ['days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'] as DurationUnit[]) {\n const v = duration[u] ?? 0;\n totalNs += fieldToNs(v, DURATION_UNIT_TO_NS[u], u);\n }\n\n // Apply rounding on the absolute value, preserving sign (same as round()).\n const stepNs = increment * DURATION_UNIT_TO_NS[options.unit];\n const sign = totalNs < 0n ? -1n : 1n;\n const abs = totalNs < 0n ? -totalNs : totalNs;\n const stepped = abs / stepNs;\n const remainder = abs % stepNs;\n let rounded: bigint;\n switch (mode) {\n case 'nearest':\n // Banker's rounding would be the Temporal spec, but Math.round-style\n // half-up is what callers typically expect from a \"round\" function.\n // Going with half-up to match round() above and avoid surprising anyone\n // who reads the output.\n rounded = remainder * 2n >= stepNs ? stepped + 1n : stepped;\n break;\n case 'floor': rounded = stepped; break;\n case 'ceil': rounded = remainder > 0n ? stepped + 1n : stepped; break;\n case 'trunc': rounded = stepped; break;\n }\n const newTotalNs = sign * rounded * stepNs;\n\n // Distribute back into the largest unit ≤ the requested unit. This\n // produces a balanced duration (e.g. rounding 90s to minutes gives\n // 1m30s in the minute field, not 90s).\n const result: DurationFields = { ...duration };\n let remaining = newTotalNs;\n // Clear all absolute units, then re-populate from largest to smallest so\n // the balanced output has all its value concentrated at the target unit\n // and below.\n const unitsInOrder: DurationUnit[] = ['days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];\n const startIdx = unitsInOrder.indexOf(options.unit);\n // Units finer than the rounding target are zeroed: all their value was\n // folded into `remaining` above, and whatever survives rounding now\n // lives at the target unit and above. (Units coarser than the target\n // are re-written by the distribution loop below — their value is\n // already inside `remaining`, so the spread copy is overwritten with\n // the same number.)\n for (let i = startIdx + 1; i < unitsInOrder.length; i++) {\n result[unitsInOrder[i]!] = 0;\n }\n // Re-populate from the largest absolute unit down to the target, so the\n // result has all its value concentrated at the target unit and above.\n for (let i = 0; i <= startIdx; i++) {\n const u = unitsInOrder[i]!;\n const unitNs = DURATION_UNIT_TO_NS[u];\n // Dead by construction: unitsInOrder only lists days/hours/minutes/\n // seconds/milliseconds/microseconds/nanoseconds, none of which are\n // 0n in DURATION_UNIT_TO_NS (only years/months/weeks are, and those\n // never appear in this array). Kept in case unitsInOrder grows to\n // include a calendar-bound unit later.\n /* c8 ignore next */\n if (unitNs === 0n) continue;\n const count = remaining / unitNs;\n remaining -= count * unitNs;\n // Assign unconditionally (zero counts write 0): the units at and above\n // the target are fully re-derived from the rounded total, so any\n // original spread-copied value must be overwritten either way —\n // conditionally skipping zero counts would leave the caller's stale\n // value sitting in the field.\n result[u] = Number(count);\n }\n return result;\n}","// Serialization / interop helpers. Standardized\n// parse/format pairs for ISO 8601, RFC 3339, RFC 2822, HTTP-date,\n// plus epoch conversions to/from unix seconds/ms/µs/ns.\n//\n// All of these wrap the existing parse()/format() surface rather than\n// reimplementing the formats — same convention as the rest of the\n// library (one tokenizer, one parser, one constructor path). The\n// standard-format helpers are essentially pre-baked format strings\n// with the right tokens, plus stricter input validation where the\n// standard demands it (e.g. RFC 3339 requires UTC-offset-only zones,\n// not named IANA zones).\n\nimport { parse } from './parse.js';\nimport { format } from './format.js';\nimport { getTemporal, type TemporalNamespace } from './temporalProvider.js';\nimport { FormatSyntaxError, InvalidDateError } from './errors.js';\n\n// Build a Temporal instance from a field bag. Used by the parse helpers\n// to construct PlainDate / PlainDateTime / ZonedDateTime / Instant from\n// the fields the standard format carries. Throws on construction errors\n// (Feb 30, etc.) wrapped in InvalidDateError so callers get the typed\n// error surface.\nfunction constructFromFields(fields: Record<string, unknown>, kind: 'PlainDate' | 'PlainDateTime' | 'ZonedDateTime' | 'Instant'): unknown {\n const temporal = getTemporal();\n try {\n switch (kind) {\n case 'PlainDate':\n return temporal.PlainDate.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'PlainDateTime':\n return temporal.PlainDateTime.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'ZonedDateTime':\n return temporal.ZonedDateTime.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'Instant': {\n // Instant.from takes an ISO string, not a field bag. Build the\n // ISO string ourselves and parse it.\n const iso = buildISOFromFields(fields);\n if (!temporal.Instant) {\n throw new Error('temporal-fmt: Temporal.Instant is not available in this implementation.');\n }\n return temporal.Instant.from(iso);\n }\n }\n } catch (err) {\n throw new InvalidDateError({ input: JSON.stringify(fields), reason: (err as Error).message });\n }\n}\n\nfunction buildISOFromFields(fields: Record<string, unknown>): string {\n const y = String(fields.year).padStart(4, '0');\n const m = String(fields.month).padStart(2, '0');\n const d = String(fields.day).padStart(2, '0');\n const h = fields.hour !== undefined ? `T${String(fields.hour).padStart(2, '0')}` : '';\n const min = fields.minute !== undefined ? `:${String(fields.minute).padStart(2, '0')}` : '';\n const sec = fields.second !== undefined ? `:${String(fields.second).padStart(2, '0')}` : '';\n const frac = fields.millisecond !== undefined && (fields.millisecond as number) > 0\n ? `.${String(fields.millisecond).padStart(3, '0')}`\n : '';\n const tz = typeof fields.timeZone === 'string' ? fields.timeZone : 'Z';\n return `${y}-${m}-${d}${h}${min}${sec}${frac}${tz}`;\n}\n\n// ISO 8601 is the most permissive of the standards here — accepts\n// calendar dates, week dates, ordinal dates, times with optional\n// fractional seconds, timezone offsets or 'Z'. Delegates to\n// Temporal's own ISO parsing (which is ISO 8601-compliant) by\n// constructing from the raw ISO string.\n\nexport function parseISO(input: string): unknown {\n const temporal = getTemporal();\n // Detect what kind of Temporal object the ISO string describes.\n // ISO 8601 strings have a date part, optional time part, optional\n // timezone. We dispatch to the right Temporal constructor based on\n // which parts are present.\n try {\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(input) || /T\\d{2}:\\d{2}/.test(input)) {\n // Has a time component.\n if (/[zZ]$/.test(input) || /[+-]\\d{2}:?\\d{2}$/.test(input)) {\n // ZonedDateTime. Temporal.ZonedDateTime.from accepts ISO with\n // a zone bracket (e.g. \"2026-08-04T15:45:30[UTC]\") but rejects\n // a bare offset with no bracket. For \"Z\" suffix, expand to\n // \"+00:00[UTC]\"; for a numeric offset, append that same offset\n // as the bracket (e.g. \"+05:30\" → \"+05:30[+05:30]\").\n let iso = input;\n if (/[zZ]$/.test(iso)) {\n iso = iso.slice(0, -1) + '+00:00[UTC]';\n } else {\n const offsetMatch = /([+-]\\d{2}:?\\d{2})$/.exec(iso);\n if (offsetMatch) {\n iso = `${iso}[${offsetMatch[1]}]`;\n }\n }\n return temporal.ZonedDateTime.from(iso as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n return temporal.PlainDateTime.from(input as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(input)) {\n return temporal.PlainDate.from(input as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n if (/^\\d{2}:\\d{2}/.test(input)) {\n return temporal.PlainTime.from(input as unknown as Record<string, number | string | undefined>);\n }\n throw new Error(`temporal-fmt: parseISO(\"${input}\") doesn't look like an ISO 8601 string.`);\n } catch (err) {\n throw new InvalidDateError({ input, reason: (err as Error).message });\n }\n}\n\nexport function formatISO(value: unknown): string {\n // Temporal objects expose toString() which produces ISO 8601 output\n // by spec. So formatting IS just toString().\n const v = value as { toString?: () => string };\n if (typeof v?.toString !== 'function') {\n throw new FormatSyntaxError({ reason: `formatISO() expects a Temporal value with toString(), got ${typeof value}.` });\n }\n return v.toString();\n}\n\n// RFC 3339 is a profile of ISO 8601 used in network protocols (HTTP,\n// JSON-RPC, etc.). Stricter than ISO 8601: requires full date+time,\n// requires seconds, requires UTC offset or 'Z', disallows IANA zone\n// names. Delegates to parseISO then validates the constraints.\n\nexport function parseRFC3339(input: string): unknown {\n // RFC 3339 grammar (https://www.rfc-editor.org/rfc/rfc3339#section-5.6):\n // date-fullyear = 4DIGIT\n // date-month = 2DIGIT ; 01-12\n // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31\n // time-hour = 2DIGIT ; 00-23\n // time-minute = 2DIGIT ; 00-59\n // time-second = 2DIGIT ; 00-58, 00-59, 00-60 (leap second)\n // time-secfrac = \".\" 1*DIGIT\n // time-numoffset = (\"+\" / \"-\") time-hour \":\" time-minute\n // time-offset = \"Z\" / time-numoffset\n // partial-time = time-hour \":\" time-minute \":\" time-second [time-secfrac]\n // full-date = date-fullyear \"-\" date-month \"-\" date-mday\n // full-time = partial-time time-offset\n // date-time = full-date \"T\" full-time\n const re = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?([Zz]|[+-]\\d{2}:\\d{2})$/;\n const m = re.exec(input);\n if (!m) {\n throw new FormatSyntaxError({ input, reason: 'does not match RFC 3339 date-time grammar' });\n }\n // Construct via parseISO with the same expansion for bare Z.\n const iso = input.replace(/[tT]/, 'T').replace(/[zZ]$/, '+00:00[UTC]');\n return parseISO(iso);\n}\n\nexport function formatRFC3339(value: unknown): string {\n // RFC 3339 output is ISO 8601 with a UTC offset. toString() on a\n // ZonedDateTime produces this shape; we just verify the result looks\n // like RFC 3339 and pass it through.\n const s = formatISO(value);\n if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$/.test(s)) {\n throw new FormatSyntaxError({ input: s, reason: 'value did not produce RFC 3339-compliant output' });\n }\n return s;\n}\n\n// RFC 2822 date-time (used in email Message-ID headers, etc.). Format:\n// day-of-week, day month year hour:minute:second zone\n// e.g. \"Mon, 04 Aug 2026 15:45:30 +0000\"\n// JS Date can parse this natively, so we use Date.parse and convert\n// to a Temporal.Instant. (Date.parse's RFC 2822 support is universal\n// across engines, unlike its ISO 8601 support which varies.)\n\nexport function parseRFC2822(input: string): unknown {\n // Strict shape pre-check: Date.parse is far more permissive than RFC 2822\n // (it happily accepts ISO 8601 and other dialects), so without this gate\n // parseRFC2822(\"2026-08-04\") would succeed despite not being an RFC 2822\n // date at all. The regex mirrors RFC 2822 §3.3's date-time grammar:\n // optional day-of-week, 1-2 digit day, month name, 2-4 digit year (the\n // obsolete two-digit form included), time with optional seconds, and a\n // zone that is either a numeric ±HHMM offset or an alphabetic zone name.\n const RFC2822_SHAPE =\n /^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?\\d{1,2}\\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+\\d{2,4}\\s+\\d{2}:\\d{2}(?::\\d{2})?\\s+(?:[+-]\\d{4}|[A-Za-z]{1,5})$/;\n if (!RFC2822_SHAPE.test(input.trim())) {\n throw new FormatSyntaxError({ input, reason: 'not a valid RFC 2822 date' });\n }\n const ms = Date.parse(input);\n if (Number.isNaN(ms)) {\n throw new FormatSyntaxError({ input, reason: 'not a valid RFC 2822 date' });\n }\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function formatRFC2822(value: unknown): string {\n // Format using format() with the right tokens, then construct via\n // the value's epoch ms. RFC 2822 requires a fixed offset, not an\n // IANA zone, so we read the value's offset directly.\n const v = value as { toEpochMilliseconds?: () => number; epochMilliseconds?: number; toInstant?: () => { epochMilliseconds: number } };\n let ms: number | undefined;\n if (typeof v?.toEpochMilliseconds === 'function') {\n ms = v.toEpochMilliseconds();\n } else if (typeof v?.epochMilliseconds === 'number') {\n // Temporal.Instant exposes epochMilliseconds as a property.\n ms = v.epochMilliseconds;\n } else if (typeof v?.toInstant === 'function') {\n // ZonedDateTime.toInstant() → Instant.\n const inst = v.toInstant();\n if (typeof (inst as { epochMilliseconds?: number }).epochMilliseconds === 'number') {\n ms = (inst as { epochMilliseconds: number }).epochMilliseconds;\n }\n }\n if (ms === undefined) {\n throw new FormatSyntaxError({ reason: `formatRFC2822() expects a value with epochMilliseconds, toEpochMilliseconds(), or toInstant(), got ${typeof value}.` });\n }\n return formatRFC2822FromMs(ms);\n}\n\nfunction formatRFC2822FromMs(ms: number): string {\n // Use Intl.DateTimeFormat with the en-US POSIX locale and the rfc822\n // format option. Modern Node ships Intl with this option; if not\n // available, fall back to manual formatting.\n try {\n const fmt = new Intl.DateTimeFormat('en-US', {\n weekday: 'short', day: '2-digit', month: 'short', year: 'numeric',\n hour: '2-digit', minute: '2-digit', second: '2-digit',\n timeZone: 'UTC', timeZoneName: 'shortOffset',\n hour12: false,\n });\n const parts = fmt.formatToParts(new Date(ms));\n /* c8 ignore start -- every part type get() is called with (weekday,\n day, month, year, hour, minute, second, timeZoneName) is always\n present given the fixed options above, so the ?? '' fallback\n can't fire without a different Intl implementation. */\n const get = (t: string) => parts.find((p) => p.type === t)?.value ?? '';\n /* c8 ignore stop */\n const tzRaw = get('timeZoneName');\n // Intl's shortOffset returns \"GMT+0\", \"GMT-8\", \"GMT+5:30\", etc.\n // RFC 2822 wants \"+0000\", \"-0800\", \"+0530\". Parse and reformat.\n const tzMatch = tzRaw?.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n let tz: string;\n if (tzMatch) {\n const sign = tzMatch[1]!;\n const hours = tzMatch[2]!.padStart(2, '0');\n const minutes = (tzMatch[3] ?? '0').padStart(2, '0');\n tz = `${sign}${hours}${minutes}`;\n /* c8 ignore start @preserve -- timeZone is hardcoded to 'UTC' above,\n which always resolves cleanly, so shortOffset always returns a\n parseable \"GMT+0\"-shaped string in this environment's ICU. This\n branch only guards against an ICU build whose shortOffset output\n doesn't match the regex; can't trigger it without mocking Intl.\n The ignore starts before the closing brace of the if-block so it\n covers the whole else-branch node c8 tracks as a single unit. */\n } else {\n tz = '+0000';\n }\n /* c8 ignore stop @preserve */\n let hour = get('hour');\n /* c8 ignore start @preserve -- hour12:false with a fixed UTC zone\n never yields \"24\" in this environment's ICU (midnight formats as\n \"00\"). Some ICU versions/locales are documented to emit \"24\" for\n hour12:false, so this stays as a defensive normalization; not\n reachable here without mocking Intl.DateTimeFormat's output. */\n if (hour === '24') hour = '00';\n /* c8 ignore stop @preserve */\n return `${get('weekday')}, ${get('day')} ${get('month')} ${get('year')} ${hour}:${get('minute')}:${get('second')} ${tz}`;\n /* c8 ignore start @preserve -- Intl.DateTimeFormat is constructed\n above with a literal, always-valid timeZone: 'UTC' and no other\n user input reaches the constructor or formatToParts call, so this\n catch can't be triggered through the public API in a normal Intl\n environment (e.g. Node with full-icu). Kept as a genuine fallback\n for runtimes with a broken or absent Intl implementation. */\n } catch {\n // Fallback: hand-rolled RFC 2822 format via Date's UTC methods.\n const d = new Date(ms);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n const wd = days[d.getUTCDay()];\n const dd = String(d.getUTCDate()).padStart(2, '0');\n const mon = months[d.getUTCMonth()]!;\n const yyyy = d.getUTCFullYear();\n const hh = String(d.getUTCHours()).padStart(2, '0');\n const mm = String(d.getUTCMinutes()).padStart(2, '0');\n const ss = String(d.getUTCSeconds()).padStart(2, '0');\n return `${wd}, ${dd} ${mon} ${yyyy} ${hh}:${mm}:${ss} +0000`;\n }\n /* c8 ignore stop @preserve */\n}\n\n// RFC 7231 defines three formats: IMF-fixdate, RFC 850, asctime.\n// The canonical format is IMF-fixdate (RFC 2822-like with required\n// seconds and UTC-only). Delegates to Date.parse for parsing and to\n// formatRFC2822 for formatting (they're nearly identical).\n\nexport function parseHTTPDate(input: string): unknown {\n const trimmed = input.trim();\n const ms = Date.parse(trimmed);\n if (Number.isNaN(ms)) {\n throw new FormatSyntaxError({ input, reason: 'not a valid HTTP-date' });\n }\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function formatHTTPDate(value: unknown): string {\n // IMF-fixdate format: \"Sun, 06 Nov 1994 08:49:37 GMT\"\n const instant = (value as { toInstant?: () => { epochMilliseconds: number } })?.toInstant?.()\n ?? (value as { epochMilliseconds?: number });\n const ms = typeof instant === 'object' && instant !== null && 'epochMilliseconds' in instant\n ? (instant as { epochMilliseconds: number }).epochMilliseconds\n : (() => { throw new FormatSyntaxError({ reason: `formatHTTPDate() expects an Instant or ZonedDateTime, got ${typeof value}.` }); })();\n const d = new Date(ms);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n const wd = days[d.getUTCDay()];\n const dd = String(d.getUTCDate()).padStart(2, '0');\n const mon = months[d.getUTCMonth()]!;\n const yyyy = d.getUTCFullYear();\n const hh = String(d.getUTCHours()).padStart(2, '0');\n const mm = String(d.getUTCMinutes()).padStart(2, '0');\n const ss = String(d.getUTCSeconds()).padStart(2, '0');\n return `${wd}, ${dd} ${mon} ${yyyy} ${hh}:${mm}:${ss} GMT`;\n}\n\n// All epoch conversions go through Temporal.Instant, which has spec\n// methods for each epoch granularity. Wrapped here for the\n// temporal-fmt surface so callers don't have to import Temporal\n// directly.\n\nfunction getInstant(value: unknown): { epochMilliseconds: number; epochNanoseconds: bigint } {\n const v = value as { toInstant?: () => { epochMilliseconds: number; epochNanoseconds: bigint }; epochMilliseconds?: number; epochNanoseconds?: bigint };\n if (typeof v?.toInstant === 'function') {\n const inst = v.toInstant();\n // Prefer the instant's own epochNanoseconds when present — same\n // precision reasoning as below, since toInstant() can return a\n // real Temporal.Instant carrying both fields.\n if (typeof (inst as { epochNanoseconds?: bigint }).epochNanoseconds === 'bigint') {\n return { epochMilliseconds: inst.epochMilliseconds, epochNanoseconds: (inst as { epochNanoseconds: bigint }).epochNanoseconds };\n }\n return { epochMilliseconds: inst.epochMilliseconds, epochNanoseconds: BigInt(inst.epochMilliseconds) * 1_000_000n };\n }\n // Check epochNanoseconds before epochMilliseconds: a real\n // Temporal.Instant exposes both, and epochMilliseconds is truncated\n // to millisecond precision. Deriving epochNanoseconds by scaling\n // epochMilliseconds back up would silently drop sub-ms precision\n // that was available on the object all along.\n if (typeof v?.epochNanoseconds === 'bigint') {\n return { epochMilliseconds: Number(v.epochNanoseconds / 1_000_000n), epochNanoseconds: v.epochNanoseconds };\n }\n if (typeof v?.epochMilliseconds === 'number') {\n return { epochMilliseconds: v.epochMilliseconds, epochNanoseconds: BigInt(v.epochMilliseconds) * 1_000_000n };\n }\n throw new FormatSyntaxError({ reason: `expected an Instant or ZonedDateTime, got ${typeof value}.` });\n}\n\nfunction requireInstant(): NonNullable<TemporalNamespace['Instant']> {\n const t = getTemporal().Instant;\n if (!t) throw new Error('temporal-fmt: Temporal.Instant is not available in this implementation.');\n return t;\n}\n\nexport function toUnixSeconds(value: unknown): number {\n return getInstant(value).epochMilliseconds / 1000;\n}\n\nexport function fromUnixSeconds(seconds: number): unknown {\n return requireInstant().fromEpochMilliseconds(seconds * 1000);\n}\n\nexport function fromUnixMilliseconds(ms: number): unknown {\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function fromUnixMicroseconds(µs: number): unknown {\n // Temporal.Instant has no fromEpochMicroseconds in the spec — only\n // fromEpochMilliseconds and fromEpochNanoseconds. Convert via ns.\n return requireInstant().fromEpochNanoseconds(BigInt(µs) * 1_000n);\n}\n\nexport function fromUnixNanoseconds(ns: bigint): unknown {\n return requireInstant().fromEpochNanoseconds(ns);\n}\n\nexport function toUnixMilliseconds(value: unknown): number {\n return getInstant(value).epochMilliseconds;\n}\n\nexport function toUnixMicroseconds(value: unknown): number {\n const { epochNanoseconds } = getInstant(value);\n return Number(epochNanoseconds / 1_000n);\n}\n\nexport function toUnixNanoseconds(value: unknown): bigint {\n return getInstant(value).epochNanoseconds;\n}\n\n// SQL-oriented helpers. SQL date/time formats are simpler than the\n// network standards above — just 'YYYY-MM-DD' for dates, 'HH:MM:SS'\n// for times, 'YYYY-MM-DD HH:MM:SS' for datetimes. Delegating to\n// parse() with the right format strings keeps the validation surface\n// consistent.\nexport function parseSQL(input: string): unknown {\n const trimmed = input.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n return parse('yyyy-MM-dd', trimmed);\n }\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return parse('yyyy-MM-dd HH:mm:ss', trimmed);\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(trimmed)) {\n return parseISO(trimmed);\n }\n throw new FormatSyntaxError({ input, reason: 'not a recognized SQL date/time format' });\n}\n\nexport function formatSQL(value: unknown): string {\n // Detect what kind of Temporal value this is and pick the format.\n const v = value as { year?: number; hour?: number };\n if (typeof v?.hour === 'number') {\n return format(value as Parameters<typeof format>[0], 'yyyy-MM-dd HH:mm:ss');\n }\n return format(value as Parameters<typeof format>[0], 'yyyy-MM-dd');\n}","// Duration extensions. The existing formatDuration()\n// in formatDuration.ts stays as-is; this module adds the rest of the\n// duration surface: parseDuration, parseISODuration, formatISODuration,\n// formatDurationToParts, balanceDuration, roundDuration (re-exported\n// from rounding.ts), totalDuration, compareDuration, addDuration,\n// subtractDuration.\n//\n// All operate on plain field bags ({ years, months, weeks, days, hours,\n// minutes, seconds, milliseconds, microseconds, nanoseconds }) rather\n// than Temporal.Duration instances — same convention as the rest of\n// this module set, so callers can use these without committing to a\n// Temporal implementation. addDuration/subtractDuration produce a new\n// field bag; totalDuration produces a number; compareDuration produces\n// -1/0/1.\n\nimport { roundDuration, fieldToNs, type DurationFields } from './rounding.js';\nimport { formatDuration, renderDurationPieces } from './formatDuration.js';\nimport { InvalidDurationError, FormatSyntaxError } from './errors.js';\n\n// Re-export roundDuration here so callers can import everything duration-\n// related from one place.\nexport { roundDuration };\nexport type { DurationFields };\n\n// Mirrors formatToParts in format.ts. Returns the same piece list\n// formatDuration() would consume, but split into parts instead of\n// joined. Useful for callers building custom UIs (one DOM node per\n// unit) or doing their own unit suppression logic.\n\nimport type { Piece } from './tokenize.js';\n\nexport interface DurationPart {\n type: 'literal' | 'token';\n value: string;\n token?: string;\n}\n\n// Delegates to formatDuration()'s own piece renderer, so the joined\n// string and the parts can never disagree. (This used to re-tokenize the\n// format string and re-derive part boundaries by searching the rendered\n// string with indexOf — which mis-sliced whenever a literal's text also\n// occurred inside an earlier token's rendered value, e.g. a \"s\"\n// separator against \"5 seconds\".) Zero-suppressed tokens emit an empty\n// token part and their neighboring literals are kept, matching the\n// documented formatDurationToParts contract.\nexport function formatDurationToParts(\n duration: Record<string, unknown>,\n formatStr: string,\n options: Parameters<typeof formatDuration>[2] = {},\n): DurationPart[] {\n return renderDurationPieces(duration, formatStr, options).map((p) =>\n p.kind === 'token' ? { type: 'token' as const, value: p.value, token: p.token } : { type: 'literal' as const, value: p.value },\n );\n}\n\n// ISO 8601 duration format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S\n// e.g. \"P3Y6M4DT12H30M5S\", \"PT1H30M\", \"P1W\", \"P0D\".\n// Parsed into a DurationFields bag.\nexport function parseISODuration(input: string): DurationFields {\n // Loose grammar: P [years Y] [months M] [weeks W] [days D] [T [hours H] [minutes M] [seconds S]]\n // Weeks is an ISO 8601-2 extension; widely supported.\n const re = /^P(?:(?:(\\d+(?:\\.\\d+)?)Y)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)W)?(?:(\\d+(?:\\.\\d+)?)D)?)?(?:T(?:(\\d+(?:\\.\\d+)?)H)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/;\n const m = re.exec(input);\n if (!m) {\n throw new InvalidDurationError({ input, reason: 'does not match ISO 8601 duration grammar (P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S)' });\n }\n // All-zero / empty: P0D is valid ISO for zero duration.\n if (m.slice(1).every((g) => g === undefined)) {\n throw new InvalidDurationError({ input, reason: 'duration has no fields (use \"P0D\" for zero duration)' });\n }\n const toNum = (s: string | undefined): number => s === undefined ? 0 : Number(s);\n return {\n years: toNum(m[1]),\n months: toNum(m[2]),\n weeks: toNum(m[3]),\n days: toNum(m[4]),\n hours: toNum(m[5]),\n minutes: toNum(m[6]),\n seconds: toNum(m[7]),\n };\n}\n\nexport function formatISODuration(duration: DurationFields): string {\n const parts: string[] = ['P'];\n const years = duration.years ?? 0;\n const months = duration.months ?? 0;\n const weeks = duration.weeks ?? 0;\n const days = duration.days ?? 0;\n const hours = duration.hours ?? 0;\n const minutes = duration.minutes ?? 0;\n const seconds = duration.seconds ?? 0;\n if (years) parts.push(`${years}Y`);\n if (months) parts.push(`${months}M`);\n if (weeks) parts.push(`${weeks}W`);\n if (days) parts.push(`${days}D`);\n if (hours || minutes || seconds) {\n parts.push('T');\n if (hours) parts.push(`${hours}H`);\n if (minutes) parts.push(`${minutes}M`);\n if (seconds) parts.push(`${seconds}S`);\n }\n // parts is just ['P'] when every field is zero -- ISO has no empty\n // duration, so fall back to P0D.\n if (parts.length === 1) parts.push('0D');\n return parts.join('');\n}\n\n// Minimal tokenizer for the duration format string. Reuses the same\n// quoted-literal and greedy-match logic as tokenize.ts.\nfunction tokenizeDurationFormat(formatStr: string): Array<Piece & { unit?: string }> {\n const DURATION_TOKEN_STRINGS = ['y', 'o', 'w', 'd', 'h', 'm', 's', 'S']\n .flatMap((single) => [single + single + single, single + single, single])\n .sort((a, b) => b.length - a.length);\n const pieces: Array<Piece & { unit?: string }> = [];\n let i = 0;\n while (i < formatStr.length) {\n const ch = formatStr[i];\n if (ch === \"'\") {\n if (formatStr[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < formatStr.length) {\n if (formatStr[j] === \"'\") {\n if (formatStr[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += formatStr[j]!;\n j += 1;\n }\n if (!closed) throw new FormatSyntaxError({ format: formatStr, reason: 'unterminated quote' });\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = DURATION_TOKEN_STRINGS.find((tok) => formatStr.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match, unit: match[0] });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n}\n\nfunction appendLiteral(pieces: Array<Piece & { unit?: string }>, 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\nfunction findNextLiteralStart(\n tokens: Array<Piece & { unit?: string }>,\n _currentTok: Piece & { unit?: string },\n full: string,\n pos: number,\n): number {\n // Find the position in `full` where the next literal after the\n // current token starts. For now, just return the end of `full` —\n // formatDurationToParts is best-effort for the common case where\n // tokens are separated by literals, and degrades gracefully when\n // they aren't.\n // A more precise implementation would walk the tokens array from\n // the current position and use the next literal piece's text to\n // find the boundary in `full`. Doing it correctly requires handling\n // zero-value unit suppression, which is why this helper exists.\n // For now, we use the next literal piece's value as a search anchor.\n const idx = tokens.indexOf(_currentTok);\n for (let j = idx + 1; j < tokens.length; j++) {\n const t = tokens[j]!;\n if (t.kind === 'literal') {\n const found = full.indexOf(t.value, pos);\n if (found >= 0) return found;\n }\n }\n return full.length;\n}\n\n// ISO 8601 duration format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S\n// Parses a duration-format string (the tokenized format formatDuration\n// accepts) into a DurationFields bag. Inverse of formatDuration().\nexport function parseDuration(input: string, formatStr: string, options: { locale?: string } = {}): DurationFields {\n // Build a capturing regex from the format string. Each token\n // captures a number; literals are matched literally.\n const tokens = tokenizeDurationFormat(formatStr);\n const fields: DurationFields = {};\n const groupSpecs: Array<{ unit: keyof DurationFields; form: 'numeric' | 'short' | 'long' }> = [];\n let regex = '';\n for (const tok of tokens) {\n if (tok.kind === 'literal') {\n regex += tok.value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n } else {\n const unit = tok.unit!;\n const form = tok.value.length === 1 ? 'numeric' : tok.value.length === 2 ? 'short' : 'long';\n // Map token letter to DurationFields key.\n /* c8 ignore start @preserve -- the final `: null` branch, and the\n `if (key === null)` throw it feeds, are both unreachable: tok.unit\n is always one of the 8 letters in DURATION_TOKEN_STRINGS (the same\n source set this ternary checks against), so `null` can't be\n produced by any token the tokenizer emits. Kept as a guard against\n a future letter being added to one set without the other. */\n const key: keyof DurationFields | null = (\n unit === 'y' ? 'years'\n : unit === 'o' ? 'months'\n : unit === 'w' ? 'weeks'\n : unit === 'd' ? 'days'\n : unit === 'h' ? 'hours'\n : unit === 'm' ? 'minutes'\n : unit === 's' ? 'seconds'\n : unit === 'S' ? 'milliseconds'\n : null\n );\n if (key === null) {\n throw new FormatSyntaxError({ format: formatStr, token: tok.value, reason: 'unknown duration token' });\n }\n /* c8 ignore stop @preserve */\n // Numeric form: capture digits (and optional sign).\n // Short/long form: capture digits followed by optional unit text.\n // We'll handle unit-text stripping after the match.\n if (form === 'numeric') {\n regex += '(-?\\\\d+)';\n } else {\n // Short form like \"2y\" or \"2yrs\"; long form like \"2 years\" or \"2 year\".\n // Capture the number; unit text is matched but not captured.\n regex += '(\\\\d+)\\\\s*\\\\w*';\n }\n groupSpecs.push({ unit: key, form });\n }\n }\n const re = new RegExp(`^${regex}$`, 'u');\n const m = re.exec(input);\n if (!m) {\n throw new FormatSyntaxError({ input, format: formatStr, reason: 'does not match duration format' });\n }\n groupSpecs.forEach((spec, i) => {\n const raw = m[i + 1]!;\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n throw new InvalidDurationError({ input, reason: `token for ${spec.unit} matched \"${raw}\" but isn't a finite number` });\n }\n fields[spec.unit] = value;\n });\n return fields;\n}\n\n// Balances a duration's absolute fields (days/hours/minutes/seconds/ms/µs/ns)\n// so each carries its natural range and excess carries into the next\n// larger unit. Doesn't touch calendar-bound fields (years/months/weeks)\n// since those need a relativeTo to balance correctly.\nexport function balanceDuration(duration: DurationFields): DurationFields {\n const NS_PER: Array<[keyof DurationFields, bigint]> = [\n ['days', 86_400n * 1_000_000_000n],\n ['hours', 3_600n * 1_000_000_000n],\n ['minutes', 60n * 1_000_000_000n],\n ['seconds', 1_000_000_000n],\n ['milliseconds', 1_000_000n],\n ['microseconds', 1_000n],\n ['nanoseconds', 1n],\n ];\n // Sum everything from days down to ns.\n let totalNs = 0n;\n for (const [k, nsPer] of NS_PER) {\n const v = duration[k];\n if (typeof v === 'number') totalNs += fieldToNs(v, nsPer, k);\n }\n // Re-distribute.\n const result: DurationFields = { ...duration };\n let remaining = totalNs;\n for (const [k, nsPer] of NS_PER) {\n const count = remaining / nsPer;\n remaining -= count * nsPer;\n result[k] = Number(count);\n }\n return result;\n}\n\n// Sums all absolute fields into a single number expressed in the\n// requested unit. Throws for calendar-bound target units (years/months/\n// weeks) since those need a relativeTo.\nexport function totalDuration(duration: DurationFields, unit: 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' | 'microseconds' | 'nanoseconds'): number {\n const NS_PER: Record<string, bigint> = {\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n };\n let totalNs = 0n;\n for (const k of Object.keys(NS_PER) as Array<keyof typeof NS_PER>) {\n const v = duration[k as keyof DurationFields];\n if (typeof v === 'number') totalNs += fieldToNs(v, NS_PER[k]!, k);\n }\n const divisor = NS_PER[unit];\n if (!divisor) {\n throw new InvalidDurationError({ reason: `totalDuration() does not support unit \"${unit}\" (calendar-bound units need a relativeTo)` });\n }\n return Number(totalNs) / Number(divisor);\n}\n\n// Compares two durations by their total absolute length. Returns -1/0/1.\nexport function compareDuration(a: DurationFields, b: DurationFields): number {\n const aNs = totalDurationNs(a);\n const bNs = totalDurationNs(b);\n if (aNs < bNs) return -1;\n if (aNs > bNs) return 1;\n return 0;\n}\n\nfunction totalDurationNs(d: DurationFields): bigint {\n const NS_PER: Record<string, bigint> = {\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n };\n let total = 0n;\n for (const k of Object.keys(NS_PER)) {\n const v = d[k as keyof DurationFields];\n if (typeof v === 'number') total += fieldToNs(v, NS_PER[k]!, k);\n }\n return total;\n}\n\n// Adds two durations field-by-field. Doesn't balance — call\n// balanceDuration() afterwards if you want a balanced result.\nexport function addDuration(a: DurationFields, b: DurationFields): DurationFields {\n const keys: Array<keyof DurationFields> = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];\n const result: DurationFields = {};\n for (const k of keys) {\n result[k] = (a[k] ?? 0) + (b[k] ?? 0);\n }\n return result;\n}\n\nexport function subtractDuration(a: DurationFields, b: DurationFields): DurationFields {\n return addDuration(a, negateDuration(b));\n}\n\nfunction negateDuration(d: DurationFields): DurationFields {\n const result: DurationFields = {};\n for (const k of Object.keys(d) as Array<keyof DurationFields>) {\n result[k] = -(d[k] ?? 0);\n }\n return result;\n}\n","// Relative time helpers. The existing formatDistance() in\n// formatDistance.ts stays; this module adds formatRelative() and\n// formatRelativeToNow().\n//\n// Semantic distinction:\n// formatDistance(date1, date2) — \"3 days ago\" / \"in 2 hours\"\n// Describes the gap between two values, delegating unit selection\n// and pluralization to Intl.RelativeTimeFormat.\n// formatRelative(date, baseDate) — \"yesterday\" / \"tomorrow\" / \"today\"\n// / \"in 3 days\" / \"last week\"\n// Calendar-relative: uses the calendar date diff, not the ms diff.\n// \"in 1 day\" means \"tomorrow\", even if it's 23 hours away.\n// formatRelativeToNow(date) — same as formatRelative(date, now)\n\nimport { differenceInDays } from './arithmetic.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface FormatRelativeOptions extends FormatOptions {\n // 'auto' (default) lets Intl.RelativeTimeFormat use natural forms like\n // \"yesterday\"/\"tomorrow\"/\"now\" when the value lands on ±1 or 0.\n // 'always' forces the strict \"1 day ago\"/\"in 1 day\" form.\n numeric?: 'always' | 'auto';\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n // Same cache shape as formatDistance.ts uses.\n const key = `${locale}|${numeric}`;\n let rtf = rtfCache.get(key);\n if (rtf) return rtf;\n if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n const oldestKey = rtfCache.keys().next().value;\n if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n }\n try {\n rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n rtfCache.set(key, rtf);\n return rtf;\n}\n\n// Calendar-relative \"describe date1 as if standing at date2\" — uses\n// differenceInDays (which counts date boundaries, not 24-hour periods),\n// so \"2026-08-04 23:59\" relative to \"2026-08-05 00:00\" reads as\n// \"yesterday\" (1 day ago), not \"1 second ago\".\n//\n// differenceInDays(a, b) returns b - a; for formatRelative(date1, date2)\n// we want date1 - date2 (so positive means date1 is in the future relative\n// to date2). Swap the argument order.\nexport function formatRelative(\n date1: unknown,\n date2: unknown,\n options: FormatRelativeOptions = {},\n): string {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const numeric = options.numeric ?? 'auto';\n const rtf = getRtf(locale, numeric);\n const dayDiff = -differenceInDays(date1, date2);\n const absDays = Math.abs(dayDiff);\n\n if (absDays === 0) {\n return rtf.format(0, 'day'); // \"now\" / \"in 0 days\"\n }\n if (absDays < 7) {\n return rtf.format(dayDiff, 'day');\n }\n if (absDays < 30) {\n return rtf.format(-Math.trunc(-dayDiff / 7), 'week');\n }\n if (absDays < 365) {\n return rtf.format(-Math.trunc(-dayDiff / 30), 'month');\n }\n return rtf.format(-Math.trunc(-dayDiff / 365), 'year');\n}\n\nexport function formatRelativeToNow(\n date: unknown,\n options: FormatRelativeOptions = {},\n): string {\n // Use the system's current date as the reference. PlainDate.from a\n // JS Date so differenceInDays sees the right shape.\n const now = new Date();\n const nowFields = {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n };\n return formatRelative(date, nowFields, options);\n}\n","// Locale registration and lookup. Extends the\n// existing registerLocaleVocab() surface in localeVocab.ts with\n// registerLocale() / getLocale() / hasLocale() and a richer LocaleVocab\n// that covers quarters, eras, ordinals, duration units, and relative-\n// time language.\n//\n// The existing localeVocab.ts stays as the implementation for months/\n// weekdays/day-periods (those are what tokens.ts's MMMM/MMM/EEEE/EEE/a\n// tokens read). This module wraps it and adds the extended fields.\n\nimport { registerLocaleVocab, getLocaleVocab, type LocaleVocab as BaseLocaleVocab } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface ExtendedLocaleVocab extends BaseLocaleVocab {\n // Quarters: Q1..Q4 long form (\"First quarter\", etc.). Default falls\n // back to \"Q1\"..\"Q4\" if not registered.\n quartersLong?: string[];\n quartersShort?: string[];\n // Eras: [\"BC\", \"AD\"] for gregory, etc. Default [\"BC\", \"AD\"].\n erasLong?: string[];\n erasShort?: string[];\n // Ordinal suffix rules: [\"st\", \"nd\", \"rd\", \"th\"] for English. Default\n // empty (the existing 'do' token has English-only ordinal logic baked\n // into tokens.ts; this field lets a custom locale override it).\n ordinals?: string[];\n // Duration unit names: long singular/plural for each unit. Used by\n // formatDuration's locale-aware path when Intl.NumberFormat isn't\n // desired (e.g. for a made-up locale Intl doesn't know about).\n durationUnits?: {\n years: [string, string];\n months: [string, string];\n weeks: [string, string];\n days: [string, string];\n hours: [string, string];\n minutes: [string, string];\n seconds: [string, string];\n milliseconds: [string, string];\n };\n // Relative-time language: words for \"ago\" / \"in\" / \"now\". Used by\n // formatRelative()'s locale-aware path when Intl.RelativeTimeFormat\n // isn't available or doesn't cover a registered locale.\n relativeTime?: {\n past: string;\n future: string;\n now: string;\n };\n}\n\n// Bounded indirectly: every entry here is added by registerLocale(), which\n// first passes registerLocaleVocab() — and THAT registry caps new locales\n// at 500 (MAX_CUSTOM_VOCABS, RangeError past it). So extendedVocabs can\n// never exceed 500 keys; no separate cap is needed (and an unreachable one\n// would only add dead branches).\nconst extendedVocabs = new Map<string, ExtendedLocaleVocab>();\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_STRING_LENGTH = 256;\n\nfunction canonicalKey(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n return locale;\n }\n}\n\nexport function registerLocale(locale: string, vocab: ExtendedLocaleVocab): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new InvalidLocaleError({ actual: String(locale), reason: 'locale string must be non-empty' });\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new InvalidLocaleError({ actual: String(locale.length), reason: `locale tag must be at most ${MAX_LOCALE_TAG_LENGTH} characters` });\n }\n // Validate the base vocab fields (months/weekdays/day-periods) via\n // the existing registerLocaleVocab — it has the strict-shape checks.\n registerLocaleVocab(locale, vocab);\n // Extended fields are optional; only validate the ones that are present.\n validateExtended(vocab, locale);\n const key = canonicalKey(locale);\n extendedVocabs.set(key, { ...vocab });\n}\n\nfunction validateExtended(vocab: ExtendedLocaleVocab, locale: string): void {\n const checkArray = (val: unknown, key: string, length: number) => {\n if (val === undefined) return;\n if (!Array.isArray(val)) {\n throw new InvalidLocaleError({ actual: String(val), reason: `locale \"${locale}\": \"${key}\" must be an array` });\n }\n if (val.length !== length) {\n throw new InvalidLocaleError({ actual: String(val.length), reason: `locale \"${locale}\": \"${key}\" must have ${length} entries (got ${val.length})` });\n }\n for (let i = 0; i < val.length; i++) {\n if (typeof val[i] !== 'string' || val[i]!.length === 0) {\n throw new InvalidLocaleError({ actual: String(val[i]), reason: `locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string` });\n }\n if (val[i]!.length > MAX_VOCAB_STRING_LENGTH) {\n throw new InvalidLocaleError({ actual: String(val[i]!.length), reason: `locale \"${locale}\": \"${key}[${i}]\" must be at most ${MAX_VOCAB_STRING_LENGTH} characters` });\n }\n }\n };\n checkArray(vocab.quartersLong, 'quartersLong', 4);\n checkArray(vocab.quartersShort, 'quartersShort', 4);\n checkArray(vocab.erasLong, 'erasLong', 2);\n checkArray(vocab.erasShort, 'erasShort', 2);\n checkArray(vocab.ordinals, 'ordinals', 4);\n}\n\nexport function getLocale(locale: string): ExtendedLocaleVocab | undefined {\n // Returns the extended vocab for a locale, OR undefined (falls back\n // to Intl). Combines the base vocab (from localeVocab.ts, which\n // includes Intl defaults) with any extended fields registered here.\n const key = canonicalKey(locale);\n const ext = extendedVocabs.get(key);\n if (ext) return ext;\n // Fall back to base vocab (Intl-derived) without extended fields.\n try {\n const base = getLocaleVocab(locale);\n return { ...base };\n } catch {\n return undefined;\n }\n}\n\nexport function hasLocale(locale: string): boolean {\n const key = canonicalKey(locale);\n return extendedVocabs.has(key);\n}\n\n// Re-export the base vocab types for callers that want them.\nexport type { LocaleVocab } from './localeVocab.js';\n","// Central configuration. Per-call options are the\n// default convention throughout this library (see formatDistance's\n// cutoffs precedent — explicitly NOT a global). This module provides\n// a typed ConfigOptions surface that callers can build once and pass\n// to any temporal-fmt function, plus a set of defaults that mirror\n// the existing per-call defaults.\n//\n// No global mutable state. The plan is explicit: \"No mysterious\n// mutable global state — this is a hard constraint, not a preference.\"\n// This module honors that: createConfig() returns an immutable config\n// object; there is no setConfig().\n\nimport { DEFAULT_LOCALE } from './tokens.js';\nimport type { NumberingSystem } from './numbering.js';\n\nexport interface TemporalFmtConfig {\n // BCP-47 locale tag. Default 'en-US'.\n locale: string;\n // Calendar identifier ('gregory', 'hebrew', etc.) or undefined to\n // use the locale's default.\n calendar?: string;\n // IANA timezone id ('America/New_York', etc.) or undefined to use\n // the system timezone.\n timezone?: string;\n // Numbering system. Default 'latn'.\n numberingSystem: NumberingSystem | string;\n // ISO week rules: 1 = Monday, 7 = Sunday. Default 1 (matches the\n // existing isoWeek.ts convention).\n firstDayOfWeek: 1 | 7;\n // Rounding mode for round()/roundDuration(). Default 'nearest'.\n roundingMode: 'nearest' | 'floor' | 'ceil' | 'trunc';\n // Disambiguation for ZonedDateTime construction during gaps/overlaps.\n // Default 'compatible' (Temporal's default).\n disambiguation: 'compatible' | 'earlier' | 'later' | 'reject';\n // Overflow mode for field construction (Feb 30 etc.).\n // Default 'reject' (matches parse()'s existing behavior).\n overflow: 'constrain' | 'reject';\n // Parse leniency. Default false (strict).\n parseLenient: boolean;\n // Duration zero-value display. Default false (omit zeros).\n durationShowZeroValues: boolean;\n}\n\nexport const DEFAULT_CONFIG: TemporalFmtConfig = {\n locale: DEFAULT_LOCALE,\n numberingSystem: 'latn',\n firstDayOfWeek: 1,\n roundingMode: 'nearest',\n disambiguation: 'compatible',\n overflow: 'reject',\n parseLenient: false,\n durationShowZeroValues: false,\n};\n\n// Creates a config object with the caller's overrides merged onto\n// the defaults. Returns a frozen object so callers can't mutate it\n// after the fact — same immutability convention as Temporal's own\n// options.\nexport function createConfig(overrides: Partial<TemporalFmtConfig> = {}): Readonly<TemporalFmtConfig> {\n const merged: TemporalFmtConfig = { ...DEFAULT_CONFIG, ...overrides };\n // Validate.\n if (typeof merged.locale !== 'string' || merged.locale.length === 0) {\n throw new Error(`temporal-fmt: config.locale must be a non-empty string (got ${String(merged.locale)}).`);\n }\n if (merged.firstDayOfWeek !== 1 && merged.firstDayOfWeek !== 7) {\n throw new Error(`temporal-fmt: config.firstDayOfWeek must be 1 (Monday) or 7 (Sunday) (got ${merged.firstDayOfWeek}).`);\n }\n if (!['nearest', 'floor', 'ceil', 'trunc'].includes(merged.roundingMode)) {\n throw new Error(`temporal-fmt: config.roundingMode \"${merged.roundingMode}\" is not recognized.`);\n }\n if (!['compatible', 'earlier', 'later', 'reject'].includes(merged.disambiguation)) {\n throw new Error(`temporal-fmt: config.disambiguation \"${merged.disambiguation}\" is not recognized.`);\n }\n if (!['constrain', 'reject'].includes(merged.overflow)) {\n throw new Error(`temporal-fmt: config.overflow \"${merged.overflow}\" is not recognized.`);\n }\n return Object.freeze(merged);\n}\n\n// Merges a per-call options object with a config. Per-call options take\n// precedence. Used by format()/parse()/etc. to fold a caller's config\n// into the per-call options they pass.\nexport function mergeWithConfig<T extends Record<string, unknown>>(\n config: Readonly<TemporalFmtConfig> | undefined,\n perCall: T,\n): T & { locale?: string; calendar?: string; timezone?: string; numberingSystem?: string; lenient?: boolean } {\n if (!config) return perCall;\n const result = { ...perCall } as T & { locale?: string; calendar?: string; timezone?: string; numberingSystem?: string; lenient?: boolean };\n // Only fill in fields the per-call options don't specify.\n if (result.locale === undefined) result.locale = config.locale;\n if (result.calendar === undefined && config.calendar !== undefined) result.calendar = config.calendar;\n if (result.timezone === undefined && config.timezone !== undefined) result.timezone = config.timezone;\n if (result.numberingSystem === undefined) result.numberingSystem = config.numberingSystem;\n if (result.lenient === undefined && config.parseLenient) result.lenient = true;\n return result;\n}","// Intervals / ranges. An interval is a pair of\n// (start, end) Temporal values with bounds semantics: open, closed,\n// or half-open. Operations: contains, overlaps, intersects, isBefore,\n// isAfter, intersection, union, difference, subtract, mergeIntervals,\n// splitInterval. Plus formatRange / formatRangeToParts with intelligent\n// collapsing.\n//\n// All operations are field-based (no instanceof), matching the rest of\n// the library. Internally uses the comparison helpers from comparison.ts\n// to order endpoints.\n\nimport { compare } from './comparison.js';\nimport { format, formatToParts, type FormattedPart } from './format.js';\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport type { FormatOptions } from './tokens.js';\n\nexport type IntervalBounds = 'open' | 'closed' | 'half-open-start' | 'half-open-end';\n\nexport interface Interval {\n start: unknown;\n end: unknown;\n bounds: IntervalBounds;\n}\n\nexport function interval(start: unknown, end: unknown, bounds: IntervalBounds = 'closed'): Interval {\n // Validate that start ≤ end, otherwise the interval is malformed.\n const cmp = compare(start, end);\n if (cmp > 0) {\n throw new Error(\n `temporal-fmt: interval start must be ≤ end (got start > end). Pass them in chronological order.`\n );\n }\n return { start, end, bounds };\n}\n\n// Inclusive containment check accounting for bounds semantics.\n// - 'closed': [start, end] — both endpoints included\n// - 'open': (start, end) — neither endpoint included\n// - 'half-open-start': (start, end] — start excluded, end included\n// - 'half-open-end': [start, end) — start included, end excluded\nexport function contains(iv: Interval, value: unknown): boolean {\n const startCmp = compare(value, iv.start);\n const endCmp = compare(value, iv.end);\n const afterStart = iv.bounds === 'open' || iv.bounds === 'half-open-end'\n ? startCmp > 0\n : startCmp >= 0;\n const beforeEnd = iv.bounds === 'open' || iv.bounds === 'half-open-start'\n ? endCmp < 0\n : endCmp <= 0;\n return afterStart && beforeEnd;\n}\n\n// Two intervals overlap iff their ranges intersect, regardless of bounds.\n// intersects() is the boolean form; intersection() returns the actual\n// overlap interval.\nexport function overlaps(a: Interval, b: Interval): boolean {\n return intersects(a, b);\n}\n\nexport function intersects(a: Interval, b: Interval): boolean {\n // a is entirely before b: a.end < b.start\n if (compare(a.end, b.start) < 0) return false;\n // a is entirely after b: a.start > b.end\n if (compare(a.start, b.end) > 0) return false;\n // Touching endpoints with both-closed bounds counts as overlap;\n // open bounds would not. To keep it simple, touching counts.\n return true;\n}\n\nexport function isBefore(a: Interval, b: Interval): boolean {\n return compare(a.end, b.start) < 0;\n}\n\nexport function isAfter(a: Interval, b: Interval): boolean {\n return compare(a.start, b.end) > 0;\n}\n\n// Returns the intersection of two intervals, or null if they don't\n// overlap. The result's bounds are the more restrictive of the two\n// inputs (closed ∩ open = open, etc.).\nexport function intersection(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) >= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) <= 0 ? a.end : b.end;\n // Compute resulting bounds: take the more restrictive at each endpoint.\n // If a.start === b.start, the more restrictive of (a.bounds, b.bounds)\n // at that endpoint wins.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n || (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n || (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the union of two intervals (the smallest interval containing\n// both), or null if they don't overlap (caller should use mergeIntervals\n// for that case).\nexport function union(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) <= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) >= 0 ? a.end : b.end;\n // Union's bounds are the less restrictive at each endpoint.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n && (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n && (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the part of `a` that is not in `b`. May produce 0, 1, or 2\n// intervals depending on the overlap shape.\n//\n// Bounds semantics: each surviving piece keeps the inclusivity of the\n// original endpoint it inherits from `a`, and the endpoint created by the\n// cut is always exclusive (it is `b`'s edge, which belongs to the removed\n// overlap). The old implementation derived both bounds from a lossy\n// helper that collapsed several of these combinations — the pieces'\n// endpoints were right, but the bounds metadata was stricter than the\n// actual remainder for open/half-open inputs.\nexport function difference(a: Interval, b: Interval): Interval[] {\n if (!intersects(a, b)) return [a];\n const result: Interval[] = [];\n // Does `a` include its own start / end?\n const aStartIncluded = a.bounds === 'closed' || a.bounds === 'half-open-end';\n const aEndIncluded = a.bounds === 'closed' || a.bounds === 'half-open-start';\n // Part of `a` before `b` starts: [a.start, b.start) — start inclusivity\n // inherited from a, cut end always exclusive.\n if (compare(a.start, b.start) < 0) {\n result.push({ start: a.start, end: b.start, bounds: aStartIncluded ? 'half-open-end' : 'open' });\n }\n // Part of `a` after `b` ends: (b.end, a.end] — cut start always\n // exclusive, end inclusivity inherited from a.\n if (compare(a.end, b.end) > 0) {\n result.push({ start: b.end, end: a.end, bounds: aEndIncluded ? 'half-open-start' : 'open' });\n }\n return result;\n}\n\n// Alias for difference() — \"subtract\" reads more naturally at some\n// call sites than \"difference\" does.\nexport const subtract = difference;\n\n// Merges a list of intervals, combining overlapping ones. Returns a\n// sorted list of disjoint intervals. The inputs are treated as\n// immutable — the merged result carries shallow copies of any interval\n// object it extends, so a caller's own `end` reference is never\n// reassigned under them (mergeIntervals used to write `last.end =\n// current.end` straight into the caller's object).\nexport function mergeIntervals(intervals: Interval[]): Interval[] {\n if (intervals.length === 0) return [];\n const sorted = [...intervals].sort((a, b) => compare(a.start, b.start));\n const result: Interval[] = [{ ...sorted[0]! }];\n for (let i = 1; i < sorted.length; i++) {\n const current = sorted[i]!;\n const last = result[result.length - 1]!;\n if (intersects(last, current) || compare(last.end, current.start) === 0) {\n // Merge into last.\n if (compare(current.end, last.end) > 0) {\n last.end = current.end;\n }\n } else {\n result.push(current);\n }\n }\n return result;\n}\n\n// Widest instant JS Date can represent (±8.64e15 ms, i.e. ±100,000,000\n// days from the Unix epoch). splitInterval rebuilds endpoints via Date,\n// so anything outside this window would silently produce Invalid Date\n// objects with NaN year/month/day fields — reject it descriptively\n// instead.\nconst MAX_DATE_MS = 8.64e15;\n\n// Splits an interval into N equal sub-intervals. Throws if N ≤ 0.\n// Equality is by ms-distance between start and end — for date ranges\n// this approximates \"equal time slices\", which is the most useful\n// interpretation. For calendar-bound splits (e.g. \"split this month\n// into weeks\"), callers should use recurrence() instead.\nexport function splitInterval(iv: Interval, n: number): Interval[] {\n if (n <= 0) throw new Error(`temporal-fmt: splitInterval requires n > 0 (got ${n}).`);\n if (n === 1) return [iv];\n const startFields = asDateFieldView(iv.start) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const endFields = asDateFieldView(iv.end) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const startMs = toMs(startFields);\n const endMs = toMs(endFields);\n if (Math.abs(startMs) > MAX_DATE_MS || Math.abs(endMs) > MAX_DATE_MS) {\n throw new RangeError(\n `temporal-fmt: splitInterval() endpoints are outside the representable Date range ` +\n `(approximately ±275,760 years). Split the interval into smaller ranges first.`\n );\n }\n const step = (endMs - startMs) / n;\n const result: Interval[] = [];\n for (let i = 0; i < n; i++) {\n const sliceStart = startMs + i * step;\n const sliceEnd = i === n - 1 ? endMs : sliceStart + step;\n result.push({\n start: fromMs(sliceStart, startFields),\n end: fromMs(sliceEnd, endFields),\n bounds: i === 0 ? iv.bounds : 'half-open-start',\n });\n }\n return result;\n}\n\nfunction toMs(v: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): number {\n // Reuse the same day-count math arithmetic.ts uses, inlined.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n return days * 86_400_000\n + (v.hour ?? 0) * 3_600_000\n + (v.minute ?? 0) * 60_000\n + (v.second ?? 0) * 1_000\n + (v.millisecond ?? 0);\n}\n\nfunction fromMs(ms: number, base: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): DateFieldView {\n const MS_PER_DAY = 86_400_000;\n const dayOverflow = Math.floor(ms / MS_PER_DAY);\n let withinDay = ms % MS_PER_DAY;\n if (withinDay < 0) withinDay += MS_PER_DAY;\n const hour = Math.floor(withinDay / 3_600_000);\n const minute = Math.floor((withinDay % 3_600_000) / 60_000);\n const second = Math.floor((withinDay % 60_000) / 1_000);\n const millisecond = withinDay % 1_000;\n // `ms` (and therefore `dayOverflow`) is already an absolute epoch-ms /\n // epoch-day value — toMs() computed it via Howard Hinnant's algorithm\n // from scratch, not as an offset from `base`. So the date only needs\n // reconstructing from the Unix epoch itself; `base` is just a template\n // for whatever extra (non-date) fields get spread into the result.\n // (Previously this added dayOverflow on top of base's own epoch-ms,\n // double-counting base's offset and landing tens of thousands of days\n // away from the intended date for any non-epoch base.)\n const newMs = dayOverflow * MS_PER_DAY;\n const d = new Date(newMs);\n return {\n ...base,\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n hour,\n minute,\n second,\n millisecond,\n };\n}\n\n// Formats an interval as a single string, collapsing shared fields.\n// E.g. \"August 4 – August 6, 2026\" rather than \"August 4, 2026 – August 6, 2026\".\n// Uses Intl.DateTimeFormat's formatRange when available (most engines),\n// falling back to format(start, fmt) + '–' + format(end, fmt) when not.\n\nexport function formatRange(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): string {\n // The formatStr parameter is this function's contract — the caller\n // asked for their token format on both endpoints. Honor it first:\n // format each side with the caller's format string and join with an\n // en dash. (This used to try Intl.DateTimeFormat.formatRange() first,\n // which ignores formatStr entirely — formatRange(iv, 'yyyy-MM-dd')\n // silently returned locale-default output like \"8/4/2026 – 8/6/2026\"\n // instead of \"2026-08-04 – 2026-08-06\".) Intl's range collapsing is\n // kept as a fallback for inputs the token path can't render.\n try {\n const startStr = format(iv.start as Parameters<typeof format>[0], formatStr, options);\n const endStr = format(iv.end as Parameters<typeof format>[0], formatStr, options);\n return `${startStr} – ${endStr}`;\n } catch (err) {\n // Fallback: Intl.DateTimeFormat.formatRange is the standard\n // mechanism with native field-collapse behavior, but it has no\n // knowledge of the caller's token format string.\n try {\n const fmt = new Intl.DateTimeFormat(normalizeLocaleTag(options.locale ?? 'en-US'), {});\n // Cast the interval endpoints to Date for the Intl call. Both\n // endpoints must be Temporal values that can convert to a Date;\n // for non-ZonedDateTime values we synthesize a Date via the field\n // shape (year/month/day/hour/...).\n const startDate = toJSDate(iv.start);\n const endDate = toJSDate(iv.end);\n return fmt.formatRange(startDate, endDate);\n } catch {\n // Neither path could render — surface the token-path error, since\n // the formatStr contract is the primary one.\n throw err;\n }\n }\n}\n\nexport function formatRangeToParts(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): FormattedPart[] {\n // Best-effort: returns formatToParts for the start, then a literal\n // \" – \" part, then formatToParts for the end. Doesn't currently\n // expose Intl.DateTimeFormat.formatRangeToParts's collapsed shape —\n // that would require mapping Intl's part types back to temporal-fmt's\n // token strings, which is locale-dependent and not stable across\n // engines.\n const startParts = formatToParts(iv.start as Parameters<typeof formatToParts>[0], formatStr, options);\n const endParts = formatToParts(iv.end as Parameters<typeof formatToParts>[0], formatStr, options);\n const result: FormattedPart[] = [...startParts];\n result.push({ type: 'literal', value: ' – ' });\n result.push(...endParts);\n return result;\n}\n\nfunction toJSDate(value: unknown): Date {\n const v = value as { year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number; toInstant?: () => { epochMilliseconds: number } };\n if (typeof v?.toInstant === 'function') {\n return new Date(v.toInstant().epochMilliseconds);\n }\n if (typeof v?.year === 'number' && typeof v?.month === 'number' && typeof v?.day === 'number') {\n return new Date(Date.UTC(v.year, v.month - 1, v.day, v.hour ?? 0, v.minute ?? 0, v.second ?? 0, v.millisecond ?? 0));\n }\n throw new Error(`temporal-fmt: formatRange() expected Temporal values with year/month/day, got ${typeof value}.`);\n}","// Recurrence engine. Deterministic RRULE-like\n// recurrence without pulling in a runtime dependency. Supports\n// secondly/minutely/hourly/daily/weekly/monthly/yearly frequencies,\n// interval, count, until, weekdays, monthDays, positional rules,\n// exclusions, inclusions.\n//\n// RRULE interop: parseRRule() / formatRRule() convert to/from the\n// standard iCalendar RRULE string format (RFC 5545). Implemented\n// without depending on a RRULE library — the grammar is small enough\n// to handle inline.\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport type RecurrenceFrequency = 'secondly' | 'minutely' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';\n\nexport interface RecurrenceRule {\n frequency: RecurrenceFrequency;\n interval: number; // default 1\n count?: number; // max occurrences\n until?: unknown; // Temporal value — recurrence ends before this\n // For weekly: ISO weekdays to include (1=Mon..7=Sun). Empty = all.\n byWeekday?: number[];\n // For monthly/yearly: days of month to include (1-31, or negative\n // for end-of-month: -1 = last day).\n byMonthDay?: number[];\n // For yearly: months to include (1-12).\n byMonth?: number[];\n // For weekly: which week of the year (1-53).\n byWeek?: number[];\n // Exclusions: dates to skip even if they match the rule.\n exDates?: unknown[];\n // Inclusions: dates to add even if they don't match the rule.\n rDates?: unknown[];\n}\n\nexport interface RecurrenceIterator {\n next(): { value: unknown; done: boolean } | { value: undefined; done: true };\n previous(): { value: unknown; done: boolean } | { value: undefined; done: true };\n}\n\n// Creates a recurrence iterator starting from `start`. The first call\n// to next() returns `start` itself (if it matches the rule); subsequent\n// calls return the next matching occurrence.\nexport function recurrence(start: unknown, rule: RecurrenceRule): RecurrenceIterator {\n let current: unknown = start;\n let count = 0;\n // Track past values for previous() — keeps a ring buffer of the last\n // N occurrences so previous() can walk back without recomputing.\n const history: unknown[] = [];\n const MAX_HISTORY = 10_000;\n let atEnd = false;\n let atStart = true;\n\n function matches(value: unknown): boolean {\n const v = value as { dayOfWeek?: number; day?: number; month?: number; year?: number; hour?: number; minute?: number; second?: number };\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) {\n if (!rule.byMonthDay.includes(v.day ?? 0)) return false;\n }\n if (rule.byMonth && rule.byMonth.length > 0) {\n if (!rule.byMonth.includes(v.month ?? 0)) return false;\n }\n if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;\n return true;\n }\n\n function advance(value: unknown, steps: number): unknown {\n // Add `steps` * interval units to `value`.\n const unit = rule.frequency === 'secondly' ? 'seconds'\n : rule.frequency === 'minutely' ? 'minutes'\n : rule.frequency === 'hourly' ? 'hours'\n : rule.frequency === 'daily' ? 'days'\n : rule.frequency === 'weekly' ? 'weeks'\n : rule.frequency === 'monthly' ? 'months'\n : 'years';\n return add(value, steps * rule.interval, unit as Parameters<typeof add>[2]);\n }\n\n function recordHistory(value: unknown): void {\n history.push(value);\n if (history.length > MAX_HISTORY) history.shift();\n }\n\n function nextMatch(value: unknown): { value: unknown; found: boolean } {\n // Advance one step at a time until we find a match. For\n // byWeekday/byMonthDay rules this can skip multiple steps.\n let candidate = value;\n let safetyCounter = 0;\n do {\n candidate = advance(candidate, 1);\n safetyCounter++;\n if (safetyCounter > 1000) {\n // Defensive — if the rule is so restrictive no match exists\n // within 1000 steps, give up rather than spin forever. The\n // caller must treat this as \"no more occurrences\", not as a\n // real match — returning the unmatched candidate here used to\n // get handed back to next()'s caller as if it were valid.\n atEnd = true;\n return { value: candidate, found: false };\n }\n if (rule.until && compare(candidate, rule.until) > 0) {\n atEnd = true;\n return { value: candidate, found: false };\n }\n } while (!matches(candidate));\n return { value: candidate, found: true };\n }\n\n return {\n next() {\n if (atEnd) return { value: undefined, done: true };\n if (atStart) {\n atStart = false;\n if (matches(current)) {\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n if (rule.until && compare(current, rule.until) > 0) {\n atEnd = true;\n return { value: undefined, done: true };\n }\n // Include rDates that match the current value.\n recordHistory(current);\n return { value: current, done: false };\n }\n // If start doesn't match, advance to first match.\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n }\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n },\n previous() {\n if (history.length === 0) return { value: undefined, done: true };\n const v = history.pop()!;\n return { value: v, done: false };\n },\n };\n}\n\n// Take N occurrences from a recurrence iterator.\nexport function take(iter: RecurrenceIterator, n: number): unknown[] {\n const result: unknown[] = [];\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n result.push(r.value);\n }\n return result;\n}\n\n// Upper bound on how many occurrences skip() will collect after its\n// skip phase. skip() used to call take(iter, Number.MAX_SAFE_INTEGER),\n// and an iterator over an unbounded rule (no count, no until — e.g.\n// { frequency: 'daily', interval: 1 }) never returns done: next() always\n// has another day. The call then looped forever, pushing into a result\n// array until the process OOM'd. Same cap style as businessCalendar.ts.\nconst MAX_SKIP_COLLECTION = 100_000;\n\n// Skip N occurrences from a recurrence iterator, returning the ones\n// that follow. Throws a RangeError when the iterator is still producing\n// occurrences after MAX_SKIP_COLLECTION — that means the rule is\n// unbounded (no count/until) and \"everything after the skip\" has no\n// finite answer.\nexport function skip(iter: RecurrenceIterator, n: number): unknown[] {\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n }\n const result: unknown[] = [];\n while (result.length < MAX_SKIP_COLLECTION) {\n const r = iter.next();\n if (r.done) return result;\n result.push(r.value);\n }\n throw new RangeError(\n `temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still ` +\n `producing — add a count or until to the rule, or use take(iter, n) for a bounded read.`\n );\n}\n\n// All occurrences between two dates (inclusive of start, exclusive of end).\nexport function between(start: unknown, rule: RecurrenceRule, rangeStart: unknown, rangeEnd: unknown): unknown[] {\n const iter = recurrence(start, rule);\n const result: unknown[] = [];\n while (true) {\n const r = iter.next();\n if (r.done) break;\n if (compare(r.value, rangeEnd) >= 0) break;\n if (compare(r.value, rangeStart) >= 0) result.push(r.value);\n }\n return result;\n}\n\n// Parses an RRULE string like \"FREQ=DAILY;INTERVAL=2;COUNT=5\" into a\n// RecurrenceRule. Doesn't support every RRULE feature (BYSETPOS,\n// BYHOUR, BYMINUTE, BYSECOND are parsed but not enforced by the\n// recurrence iterator above), but covers the common cases.\nexport function parseRRule(input: string): RecurrenceRule {\n const parts = input.trim().toUpperCase().replace(/^RRULE:/, '').split(';');\n const rule: RecurrenceRule = { frequency: 'daily', interval: 1 };\n const frequencies: ReadonlySet<string> = new Set([\n 'SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY',\n ]);\n for (const part of parts) {\n if (!part) continue;\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n switch (key) {\n case 'FREQ':\n if (!frequencies.has(value)) {\n throw new RangeError(`temporal-fmt: unsupported RRULE frequency \"${value}\".`);\n }\n rule.frequency = value.toLowerCase() as RecurrenceFrequency;\n break;\n case 'INTERVAL': {\n const interval = Number(value);\n if (!Number.isSafeInteger(interval) || interval < 1) {\n throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got \"${value}\").`);\n }\n rule.interval = interval;\n break;\n }\n case 'COUNT': {\n const count = Number(value);\n if (!Number.isSafeInteger(count) || count < 1) {\n throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got \"${value}\").`);\n }\n rule.count = count;\n break;\n }\n case 'UNTIL':\n rule.until = value;\n break;\n case 'BYDAY':\n rule.byWeekday = value.split(',').map((d) => {\n const m = d.match(/^([+-]?\\d)?([A-Z]{2})$/);\n if (!m) return 0;\n const wd = m[2];\n const map: Record<string, number> = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };\n return map[wd!] ?? 0;\n });\n break;\n case 'BYMONTHDAY': {\n const values = value.split(',').map(Number);\n if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);\n }\n rule.byMonthDay = values;\n break;\n }\n case 'BYMONTH': {\n const values = value.split(',').map(Number);\n if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);\n }\n rule.byMonth = values;\n break;\n }\n }\n }\n return rule;\n}\n\nexport function formatRRule(rule: RecurrenceRule): string {\n const parts: string[] = [`FREQ=${rule.frequency.toUpperCase()}`];\n if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);\n if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);\n if (rule.until !== undefined) parts.push(`UNTIL=${String(rule.until)}`);\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n const map: Record<number, string> = { 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' };\n parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(',')}`);\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(',')}`);\n if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(',')}`);\n return parts.join(';');\n}","// Business calendar. Customizable weekend definitions, holiday set,\n// observed holidays, working hours, optional half days. The holiday\n// check is delegated to a holiday calendar (holidays.ts) so this\n// module stays focused on weekday-vs-weekend logic.\n\nimport { add } from './arithmetic.js';\nimport type { HolidayCalendar } from './holidays.js';\n\nexport interface BusinessCalendarOptions {\n // Default [6, 7] (Sat/Sun). ISO weekdays 1-7.\n weekend?: number[];\n // Holiday calendar to consult. Optional — if absent, only weekend\n // logic applies.\n holidays?: HolidayCalendar;\n // Working hours per weekday. 1=Mon..7=Sun. Default: 8 hours for\n // weekdays, 0 for weekend. Used by addBusinessDays() to compute\n // partial-day arithmetic.\n workingHours?: Record<number, number>;\n // Half days: weekdays where working hours are reduced. Default empty.\n halfDays?: number[];\n}\n\nexport interface BusinessCalendar {\n weekend: Set<number>;\n holidays?: HolidayCalendar;\n workingHours: Record<number, number>;\n halfDays: Set<number>;\n}\n\nexport function createBusinessCalendar(options: BusinessCalendarOptions = {}): BusinessCalendar {\n const weekend = new Set(options.weekend ?? [6, 7]);\n // Copy before filling defaults — writing the missing weekday entries\n // straight into options.workingHours mutated the caller's object\n // (their { 1: 6 } grew keys 2..7 as a side effect of \"creating\" a\n // calendar, and a shared object across calendars cross-contaminated\n // them with the first calendar's defaults).\n const workingHours: Record<number, number> = { ...(options.workingHours ?? {}) };\n for (let i = 1; i <= 7; i++) {\n if (workingHours[i] === undefined) {\n workingHours[i] = weekend.has(i) ? 0 : 8;\n }\n }\n return {\n weekend,\n holidays: options.holidays,\n workingHours,\n halfDays: new Set(options.halfDays ?? []),\n };\n}\n\nexport function isBusinessDay(cal: BusinessCalendar, value: unknown): boolean {\n const v = value as { dayOfWeek?: number; year?: number; month?: number; day?: number };\n // add() in arithmetic.ts doesn't update dayOfWeek (it returns a field\n // bag with whatever dayOfWeek the input had). Compute it from year/month/day\n // whenever those are present — more reliable than trusting the cached\n // dayOfWeek value.\n let dow = v.dayOfWeek;\n if (typeof v.year === 'number' && typeof v.month === 'number' && typeof v.day === 'number') {\n const d = new Date(Date.UTC(v.year, v.month - 1, v.day));\n const jsDow = d.getUTCDay(); // 0=Sun..6=Sat\n dow = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n }\n if (typeof dow !== 'number') {\n throw new Error('temporal-fmt: isBusinessDay() needs a value with dayOfWeek (or year/month/day to compute it).');\n }\n if (cal.weekend.has(dow)) return false;\n if (cal.holidays && cal.holidays.isHoliday(value)) return false;\n return true;\n}\n\nexport function nextBusinessDay(cal: BusinessCalendar, value: unknown): unknown {\n let candidate = add(value, 1, 'days');\n // Loop until we find a business day. Capped at 7 iterations since a\n // week is the natural upper bound for a Sat/Sun + holiday combo.\n for (let i = 0; i < 14; i++) {\n if (isBusinessDay(cal, candidate)) return candidate;\n candidate = add(candidate, 1, 'days');\n }\n throw new Error('temporal-fmt: nextBusinessDay() gave up after 14 days — likely a misconfigured calendar (e.g. all days are weekends or holidays).');\n}\n\nexport function previousBusinessDay(cal: BusinessCalendar, value: unknown): unknown {\n let candidate = add(value, -1, 'days');\n for (let i = 0; i < 14; i++) {\n if (isBusinessDay(cal, candidate)) return candidate;\n candidate = add(candidate, -1, 'days');\n }\n throw new Error('temporal-fmt: previousBusinessDay() gave up after 14 days — likely a misconfigured calendar.');\n}\n\nconst MAX_BUSINESS_DAY_TRAVERSAL = 100_000;\n\nfunction assertBusinessDayCount(days: number): void {\n if (!Number.isSafeInteger(days)) {\n throw new RangeError(\n `temporal-fmt: business-day count must be a finite safe integer (got ${days}).`,\n );\n }\n}\n\nfunction compareDateFields(a: unknown, b: unknown): number {\n const av = a as { year: number; month: number; day: number };\n const bv = b as { year: number; month: number; day: number };\n const ay = av.year * 10000 + av.month * 100 + av.day;\n const by = bv.year * 10000 + bv.month * 100 + bv.day;\n return ay - by;\n}\n\nexport function addBusinessDays(cal: BusinessCalendar, value: unknown, days: number): unknown {\n assertBusinessDayCount(days);\n if (days === 0) return value;\n const sign = days > 0 ? 1 : -1;\n const absDays = Math.abs(days);\n if (absDays > MAX_BUSINESS_DAY_TRAVERSAL) {\n throw new RangeError(\n `temporal-fmt: addBusinessDays() exceeded the ${MAX_BUSINESS_DAY_TRAVERSAL}-day limit.`,\n );\n }\n let candidate = value;\n for (let i = 0; i < absDays; i++) {\n candidate = sign > 0 ? nextBusinessDay(cal, candidate) : previousBusinessDay(cal, candidate);\n }\n return candidate;\n}\n\nexport function subtractBusinessDays(cal: BusinessCalendar, value: unknown, days: number): unknown {\n assertBusinessDayCount(days);\n return addBusinessDays(cal, value, -days);\n}\n\nexport function differenceInBusinessDays(cal: BusinessCalendar, a: unknown, b: unknown): number {\n const rawDirection = compareDateFields(b, a);\n if (rawDirection === 0) return 0;\n\n const direction = rawDirection > 0 ? 1 : -1;\n let candidate = a;\n let count = 0;\n let traversedDays = 0;\n\n while (compareDateFields(candidate, b) !== 0) {\n candidate = add(candidate, direction, 'days');\n traversedDays++;\n if (traversedDays > MAX_BUSINESS_DAY_TRAVERSAL) {\n throw new RangeError(\n `temporal-fmt: differenceInBusinessDays() exceeded the ${MAX_BUSINESS_DAY_TRAVERSAL}-day traversal limit.`,\n );\n }\n if (isBusinessDay(cal, candidate)) count += direction;\n }\n\n return count;\n}\n","// Holiday framework. Core provides the abstraction only —\n// country-specific holiday datasets stay out of core; those can be\n// separate ecosystem packages built on top of this. A holiday calendar\n// is a function from a date → boolean (\"is this a holiday?\") plus\n// iteration helpers (nextHoliday, previousHoliday, holidaysBetween).\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport interface HolidayCalendar {\n isHoliday(value: unknown): boolean;\n // Returns the list of holidays within [start, end] inclusive.\n holidaysBetween(start: unknown, end: unknown): unknown[];\n}\n\nexport interface HolidaySpec {\n // Month + day for fixed-date holidays (e.g. Jan 1 → New Year's).\n month?: number;\n day?: number;\n // For floating holidays: a function that takes a year and returns\n // the { month, day } of the holiday in that year. Used for \"last\n // Monday of May\" style rules.\n compute?: (year: number) => { month: number; day: number };\n // Optional name (for introspection / debugging).\n name?: string;\n}\n\n// Upper bound on the year range holidaysBetween() will walk. The loop\n// is year-granular with no input validation on the endpoints, so a\n// caller passing (say) year 1 to year 300000 makes it iterate — and\n// cache a holiday list — for every year in between; hostile or\n// accidental huge ranges used to hang the process and grow the\n// per-calendar cache without bound. 5,000 years matches the traversal\n// caps businessCalendar.ts and recurrence.ts already use for the same\n// class of \"walk it one step at a time\" APIs.\nconst MAX_HOLIDAY_YEAR_RANGE = 5_000;\n\nexport function createHolidayCalendar(specs: HolidaySpec[]): HolidayCalendar {\n // Pre-compute holidays for a year on demand, cache by year.\n const cache = new Map<number, Array<{ month: number; day: number; name?: string }>>();\n function forYear(year: number) {\n let list = cache.get(year);\n if (list) return list;\n list = specs.map((s) => {\n let md: { month: number; day: number };\n if (s.compute) {\n md = s.compute(year);\n } else {\n md = { month: s.month!, day: s.day! };\n }\n return { ...md, name: s.name };\n });\n cache.set(year, list);\n return list;\n }\n return {\n isHoliday(value: unknown) {\n const v = value as { year?: number; month?: number; day?: number };\n if (typeof v.year !== 'number' || typeof v.month !== 'number' || typeof v.day !== 'number') {\n throw new Error('temporal-fmt: isHoliday() needs a value with year/month/day.');\n }\n const list = forYear(v.year);\n return list.some((h) => h.month === v.month && h.day === v.day);\n },\n holidaysBetween(start: unknown, end: unknown) {\n const sv = start as { year: number; month: number; day: number };\n const ev = end as { year: number; month: number; day: number };\n if (typeof sv?.year !== 'number' || typeof ev?.year !== 'number') {\n throw new Error('temporal-fmt: holidaysBetween() needs start/end values with year/month/day.');\n }\n if (ev.year - sv.year > MAX_HOLIDAY_YEAR_RANGE) {\n throw new RangeError(\n `temporal-fmt: holidaysBetween() year range (${sv.year}–${ev.year}) exceeds the ` +\n `${MAX_HOLIDAY_YEAR_RANGE}-year limit. Split the query into smaller ranges.`\n );\n }\n const result: unknown[] = [];\n for (let y = sv.year; y <= ev.year; y++) {\n const list = forYear(y);\n for (const h of list) {\n const date = { year: y, month: h.month, day: h.day };\n if (compare(date, sv) >= 0 && compare(date, ev) <= 0) {\n result.push({ year: y, month: h.month, day: h.day });\n }\n }\n }\n return result;\n },\n };\n}\n\n// Returns the next holiday strictly after `value`. Returns undefined\n// if no holiday is registered within the next 5 years (sanity cap).\nexport function nextHoliday(cal: HolidayCalendar, value: unknown): unknown | undefined {\n let candidate = add(value, 1, 'days');\n for (let i = 0; i < 365 * 5; i++) {\n if (cal.isHoliday(candidate)) return candidate;\n candidate = add(candidate, 1, 'days');\n }\n return undefined;\n}\n\nexport function previousHoliday(cal: HolidayCalendar, value: unknown): unknown | undefined {\n let candidate = add(value, -1, 'days');\n for (let i = 0; i < 365 * 5; i++) {\n if (cal.isHoliday(candidate)) return candidate;\n candidate = add(candidate, -1, 'days');\n }\n return undefined;\n}\n\n// holidaysBetween is exposed as a HolidayCalendar method (see above);\n// this standalone helper delegates to it for ergonomic access.\nexport function holidaysBetween(cal: HolidayCalendar, start: unknown, end: unknown): unknown[] {\n return cal.holidaysBetween(start, end);\n}\n","// Time-zone subsystem. Wraps Temporal.ZonedDateTime's\n// timezone introspection surface in temporal-fmt's field-based convention.\n// Includes disambiguation modes (compatible/earlier/later/reject) for\n// DST gaps and overlaps, plus transition queries (getNextTransition,\n// getPreviousTransition, getTransitions).\n\nimport { getTemporal } from './temporalProvider.js';\nimport { add } from './arithmetic.js';\n\nexport type DisambiguationMode = 'compatible' | 'earlier' | 'later' | 'reject';\n\nexport interface ResolveZonedOptions {\n disambiguation?: DisambiguationMode;\n overflow?: 'constrain' | 'reject';\n offset?: 'use' | 'ignore' | 'prefer' | 'reject';\n}\n\n// Resolves a wall-clock time in a timezone, applying disambiguation\n// for DST gaps and overlaps.\nexport function resolveZoned(\n fields: { year: number; month: number; day: number; hour?: number; minute?: number; second?: number; millisecond?: number; microsecond?: number; nanosecond?: number },\n timeZone: string,\n options: ResolveZonedOptions = {},\n): unknown {\n const temporal = getTemporal();\n const disambiguation = options.disambiguation ?? 'compatible';\n const overflow = options.overflow ?? 'constrain';\n // Cast options to a permissive record — Temporal.ZonedDateTime.from\n // accepts disambiguation/offset options, but the existing\n // TemporalFactory.from() signature in temporalProvider.ts is narrower.\n // The runtime accepts the wider shape; this cast bypasses the type\n // narrowing without changing runtime behavior.\n const fromOptions = {\n overflow,\n disambiguation,\n offset: options.offset ?? 'prefer',\n } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1];\n try {\n return temporal.ZonedDateTime.from(\n {\n year: fields.year,\n month: fields.month,\n day: fields.day,\n hour: fields.hour ?? 0,\n minute: fields.minute ?? 0,\n second: fields.second ?? 0,\n millisecond: fields.millisecond ?? 0,\n microsecond: fields.microsecond ?? 0,\n nanosecond: fields.nanosecond ?? 0,\n timeZone,\n },\n fromOptions,\n );\n } catch (err) {\n // Surface DST gap / overlap errors with a clearer message.\n const msg = (err as Error).message;\n if (/no such (wall-clock )?time/i.test(msg) && disambiguation === 'reject') {\n throw new Error(\n `temporal-fmt: \"${timeZone}\" has no such wall-clock time on ${fields.year}-${fields.month}-${fields.day}T${fields.hour ?? 0}:${fields.minute ?? 0} — ` +\n `it falls in a DST gap. Pass { disambiguation: 'compatible' | 'earlier' | 'later' } to pick an instant.`\n );\n }\n throw err;\n }\n}\n\nexport function getTimeZone(value: unknown): string {\n const v = value as { timeZoneId?: string };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTimeZone() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.timeZoneId;\n}\n\nexport function getOffset(value: unknown): string {\n const v = value as { offset?: string };\n if (typeof v?.offset !== 'string') {\n throw new Error(`temporal-fmt: getOffset() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.offset;\n}\n\nexport function getOffsetNanoseconds(value: unknown): number {\n const v = value as { offsetNanoseconds?: number; offset?: string };\n if (typeof v?.offsetNanoseconds === 'number') return v.offsetNanoseconds;\n if (typeof v?.offset === 'string') {\n // Parse ±HH:MM → nanoseconds.\n const m = v.offset.match(/^([+-])(\\d{2}):(\\d{2})$/);\n if (!m) throw new Error(`temporal-fmt: getOffsetNanoseconds() couldn't parse offset \"${v.offset}\".`);\n const sign = m[1] === '-' ? -1 : 1;\n return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60) * 1_000_000_000;\n }\n throw new Error(`temporal-fmt: getOffsetNanoseconds() expected a ZonedDateTime, got ${typeof value}.`);\n}\n\n// DST detection: a ZonedDateTime is in DST iff the offset is different\n// from the timezone's standard offset at the same instant. Computed\n// by comparing the current offset to the offset in January (Northern\n// Hemisphere) or July (Southern Hemisphere) of the same year — a\n// heuristic that's right for most populated zones.\nexport function isDST(value: unknown): boolean {\n const v = value as { timeZoneId?: string; offset?: string; year?: number; toInstant?: () => unknown };\n if (typeof v?.timeZoneId !== 'string' || typeof v?.offset !== 'string' || typeof v?.year !== 'number') {\n throw new Error(`temporal-fmt: isDST() expected a ZonedDateTime, got ${typeof value}.`);\n }\n // Compute the offset for January 1 of the same year (standard time\n // for Northern Hemisphere zones) and compare. For Southern Hemisphere\n // zones January is summer, so we'd want July instead — but checking\n // January vs current is good enough for the common case, and we\n // can't reliably determine hemisphere without a lookup table.\n const temporal = getTemporal();\n try {\n const jan = temporal.ZonedDateTime.from(\n { year: v.year, month: 1, day: 1, hour: 12, timeZone: v.timeZoneId },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n return jan.offset !== v.offset;\n } catch {\n return false;\n }\n}\n\n// Returns the next DST transition strictly after `value`, or undefined\n// if no transition occurs within the next 2 years. Implemented by\n// walking day-by-day checking for offset changes — slow but simple,\n// and correct across all IANA zones.\nexport function getNextTransition(value: unknown): unknown | undefined {\n return findTransition(value, 1);\n}\n\nexport function getPreviousTransition(value: unknown): unknown | undefined {\n return findTransition(value, -1);\n}\n\nfunction findTransition(value: unknown, direction: 1 | -1): unknown | undefined {\n const v = value as { timeZoneId?: string; offset?: string; toInstant?: () => unknown; year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: ${direction > 0 ? 'getNextTransition' : 'getPreviousTransition'}() expected a ZonedDateTime, got ${typeof value}.`);\n }\n const temporal = getTemporal();\n const timeZone = v.timeZoneId;\n let candidateFields = add(value, direction, 'days');\n const startOffset = v.offset!;\n for (let i = 0; i < 365 * 2; i++) {\n // Rebuild a real ZonedDateTime for this candidate day, in this\n // timezone, so `.offset` reflects the actual UTC offset on that\n // day rather than being absent. add() only produces a plain field\n // bag (it has no notion of timezone), so re-attaching timeZone via\n // ZonedDateTime.from is what actually lets us detect a DST change\n // here — using the field bag's own (nonexistent) offset would make\n // every call \"find\" a transition on the very first day checked.\n const cf = candidateFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const candidateZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n if (candidateZdt.offset !== startOffset) {\n // Found a transition day. Return the reconstructed ZonedDateTime.\n return candidateZdt;\n }\n candidateFields = add(candidateFields, direction, 'days');\n }\n return undefined;\n}\n\nexport function getTransitions(start: unknown, end: unknown): unknown[] {\n const result: unknown[] = [];\n const startV = start as { timeZoneId?: string; offset?: string; year?: number; month?: number; day?: number };\n if (typeof startV?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTransitions() expected a ZonedDateTime for start, got ${typeof start}.`);\n }\n const endV = end as { year: number; month: number; day: number };\n const temporal = getTemporal();\n const timeZone = startV.timeZoneId;\n let cursorFields = add(start, 1, 'days');\n let lastOffset = startV.offset!;\n for (let i = 0; i < 365 * 5; i++) {\n // Same reconstruction as findTransition() above — add() strips\n // timezone/offset info, so we rebuild a real ZonedDateTime each\n // iteration to read its actual offset instead of comparing against\n // a field bag that never has one.\n const cf = cursorFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const cursorZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string; year: number; month: number; day: number };\n if (cursorZdt.offset !== lastOffset) {\n // Found a transition.\n if (cursorZdt.year > endV.year || (cursorZdt.year === endV.year && (cursorZdt.month > endV.month || (cursorZdt.month === endV.month && cursorZdt.day > endV.day)))) break;\n result.push(cursorZdt);\n lastOffset = cursorZdt.offset;\n }\n cursorFields = add(cursorFields, 1, 'days');\n }\n return result;\n}\n\n// Returns the list of possible instants for a wall-clock time in a\n// zone — used to detect gaps (0 instants) and overlaps (2 instants).\nexport function possibleInstantsFor(\n fields: { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number },\n timeZone: string,\n): unknown[] {\n const temporal = getTemporal();\n // Resolve the same wall-clock fields twice, forcing disambiguation\n // toward the earlier and later side of any DST boundary. Comparing\n // the two results tells us which of the three cases we're in:\n // - gap (e.g. 2:30am on a spring-forward day): neither result's own\n // wall-clock fields match what was requested, since Temporal has\n // to shift off the nonexistent time in both directions — 0 instants.\n // - overlap (e.g. 1:30am on a fall-back day): both results keep the\n // requested wall-clock time but land on different instants (one\n // per side of the boundary) — 2 instants.\n // - normal time: both results match the requested wall-clock time\n // and resolve to the same instant — 1 instant.\n const earlier = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'earlier', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n const later = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'later', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n\n const matchesRequestedWallClock = (z: typeof earlier) =>\n z.year === fields.year && z.month === fields.month && z.day === fields.day &&\n z.hour === fields.hour && z.minute === fields.minute &&\n z.second === fields.second && z.millisecond === fields.millisecond;\n\n if (!matchesRequestedWallClock(earlier) || !matchesRequestedWallClock(later)) {\n // Gap — the requested wall-clock time doesn't exist in this zone.\n return [];\n }\n if (earlier.toInstant().epochNanoseconds === later.toInstant().epochNanoseconds) {\n // Normal time — both disambiguation directions agree on the instant.\n return [earlier];\n }\n // Overlap — same wall-clock time, two distinct instants.\n return [earlier, later];\n}","// Extensibility. Controlled extensibility via\n// createFormatter({ tokens: ... }) rather than uncontrolled global\n// mutation. Custom tokens are passed at formatter-creation time;\n// built-in behavior stays deterministic regardless of what's registered.\n//\n// The existing registerLocaleVocab() is a global-registration escape\n// hatch for the locale-vocabulary case; createFormatter is the\n// per-formatter alternative for custom tokens.\n\nimport { type FormattedPart, type CompiledFormat } from './format.js';\nimport { TOKENS, type TemporalLike, type FormatOptions, DEFAULT_LOCALE } from './tokens.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { UnknownTokenError } from './errors.js';\n\nexport type TokenHandler = (t: TemporalLike, locale: string) => string;\nexport type TokenField = keyof TemporalLike;\n\nexport interface CustomToken {\n name: string;\n handler: TokenHandler;\n field: TokenField;\n}\n\nexport interface FormatterOptions {\n // Custom tokens to add (or override). Built-in tokens with the same\n // name are replaced by the custom one.\n tokens?: CustomToken[];\n // Default locale for this formatter. Callers can still override per-call.\n defaultLocale?: string;\n}\n\nexport interface Formatter {\n format(temporal: TemporalLike, formatStr: string, options?: FormatOptions): string;\n formatToParts(temporal: TemporalLike, formatStr: string, options?: FormatOptions): FormattedPart[];\n compileFormat(formatStr: string): CompiledFormat;\n}\n\nexport function createFormatter(options: FormatterOptions = {}): Formatter {\n // Build a merged token table: built-ins plus overrides.\n const mergedTokens: Array<[string, TokenHandler, TokenField]> = [...TOKENS];\n const customTokenNames = new Set<string>();\n for (const custom of options.tokens ?? []) {\n customTokenNames.add(custom.name);\n }\n // Replace existing tokens with the same name; append new ones.\n for (const custom of options.tokens ?? []) {\n const existingIdx = mergedTokens.findIndex(([name]) => name === custom.name);\n if (existingIdx >= 0) {\n mergedTokens[existingIdx] = [custom.name, custom.handler, custom.field];\n } else {\n mergedTokens.push([custom.name, custom.handler, custom.field]);\n }\n }\n // Sort longest-first for the greedy tokenizer.\n mergedTokens.sort((a, b) => b[0].length - a[0].length);\n const handlerByToken = new Map(mergedTokens.map(([name, handler, field]) => [name, { handler, field }]));\n const sortedTokenStrings = mergedTokens.map(([name]) => name);\n\n function customTokenize(formatStr: string) {\n const pieces: Array<{ kind: 'token' | 'literal'; value: string }> = [];\n let i = 0;\n while (i < formatStr.length) {\n const ch = formatStr[i];\n if (ch === \"'\") {\n // Quoted literal handling — same as tokenize.ts.\n if (formatStr[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < formatStr.length) {\n if (formatStr[j] === \"'\") {\n if (formatStr[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += formatStr[j]!;\n j += 1;\n }\n if (!closed) throw new Error(`temporal-fmt: unterminated quote in format string \"${formatStr}\"`);\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = sortedTokenStrings.find((tok) => formatStr.startsWith(tok, i));\n if (match) {\n // Overlong-run guard, same rule as tokenize.ts: a greedy match of N\n // chars followed by one more of the same letter (\"MMMMM\", \"ddddd\")\n // is not a valid token of any width — emitting it as \"token +\n // literal\" would silently splice an unrelated field onto this one,\n // exactly the misreading the main tokenizer rejects. Same\n // UnknownTokenError shape tokenize.ts throws.\n const runChar = match[match.length - 1];\n if (formatStr[i + match.length] === runChar) {\n let end = i + match.length;\n while (formatStr[end] === runChar) end += 1;\n throw new UnknownTokenError({\n token: formatStr.slice(i, end),\n format: formatStr,\n message:\n `temporal-fmt: \"${formatStr.slice(i, end)}\" in format string \"${formatStr}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n }\n\n function appendLiteral(pieces: Array<{ kind: 'token' | 'literal'; value: string }>, 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\n const defaultLocale = options.defaultLocale ?? DEFAULT_LOCALE;\n\n return {\n format(temporal: TemporalLike, formatStr: string, opts: FormatOptions = {}) {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const locale = opts.locale ?? defaultLocale;\n const pieces = customTokenize(formatStr);\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable: customTokenize only ever produces a\n `kind: 'token'` piece via sortedTokenStrings.find(...), and\n handlerByToken is built from that same mergedTokens array, so\n every token piece is guaranteed a handler entry. Guards against a\n future customTokenize change that decouples the two lookups. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\", which this Temporal object doesn't have.`\n );\n }\n result += handler.handler(temporal, locale);\n }\n return result;\n },\n formatToParts(temporal: TemporalLike, formatStr: string, opts: FormatOptions = {}) {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const locale = opts.locale ?? defaultLocale;\n const pieces = customTokenize(formatStr);\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore start @preserve -- unreachable: customTokenize's own\n appendLiteral() already merges adjacent literal chars/quoted\n spans into a single piece before this loop ever sees them, so\n two literal pieces are never adjacent here. Kept as a guard\n against a future customTokenize change that stops merging. */\n if (last && last.type === 'literal') last.value += piece.value;\n /* c8 ignore stop @preserve */\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable: customTokenize only emits\n kind:'token' pieces found in sortedTokenStrings, the same array\n handlerByToken is built from, so every token piece is guaranteed\n a handler entry. Guards against future decoupling of the two. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n out.push({ type: 'token', value: handler.handler(temporal, locale), token: piece.value });\n }\n return out;\n },\n compileFormat(formatStr: string): CompiledFormat {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const pieces = customTokenize(formatStr);\n return {\n formatStr,\n pieces: pieces.map((p) => p as { kind: 'token' | 'literal'; value: string }),\n format(temporal: TemporalLike, opts: FormatOptions = {}) {\n const locale = opts.locale ?? defaultLocale;\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the same guard's comment\n above in the un-compiled format(). */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n result += handler.handler(temporal, locale);\n }\n return result;\n },\n formatToParts(temporal: TemporalLike, opts: FormatOptions = {}) {\n const locale = opts.locale ?? defaultLocale;\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore start @preserve -- unreachable, see the same guard's\n comment above in the un-compiled formatToParts. */\n if (last && last.type === 'literal') last.value += piece.value;\n /* c8 ignore stop @preserve */\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the same guard's\n comment above in the un-compiled formatToParts. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n out.push({ type: 'token', value: handler.handler(temporal, locale), token: piece.value });\n }\n return out;\n },\n };\n },\n };\n}\n\n","// IDE tooling data exports. Provides structured data editor plugins can\n// consume for: token autocomplete, hover docs, inline diagnostics,\n// quick fixes, format preview, token conversion hints from\n// Day.js/date-fns, documentation links.\n//\n// This is the data layer only — an actual VS Code extension is out of\n// scope for this package. A separate package can build the extension\n// on top of these exports.\n\nimport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\nimport { analyzeFormat, listTokens } from './analyze.js';\nimport { format as builtinFormat } from './format.js';\n\nexport interface TokenAutocompleteEntry {\n label: string;\n detail: string;\n documentation: string;\n // Sort group — same-family tokens sort together in the autocomplete list.\n family: string;\n}\n\n// Token family grouping for autocomplete organization: year, month,\n// day, etc.\nfunction tokenFamily(name: string): string {\n if (/^y+$/.test(name)) return 'Year';\n if (/^[ML]+$/.test(name)) return 'Month';\n if (/^[decD]+$/.test(name)) return 'Day';\n if (/^[Ec]+$/.test(name)) return 'Weekday';\n if (/^[Qq]+$/.test(name)) return 'Quarter';\n if (/^[Gg]+$/.test(name)) return 'Era';\n if (/^[Hh]+$/.test(name)) return 'Hour';\n if (/^m+$/.test(name)) return 'Minute';\n if (/^s+$/.test(name)) return 'Second';\n if (/^S+$/.test(name)) return 'Fractional Second';\n if (name === 'a') return 'Day Period';\n if (name === 'zzz') return 'Time Zone';\n if (/^[Xx]+$/.test(name)) return 'UTC Offset';\n if (name === 'do') return 'Ordinal Day';\n if (name === 'ww' || name === 'RRRR') return 'ISO Week';\n return 'Other';\n}\n\nexport function getAutocompleteData(): TokenAutocompleteEntry[] {\n return ALL_TOKEN_NAMES.map((name) => {\n const meta = TOKEN_METADATA[name]!;\n return {\n label: name,\n detail: meta.meaning,\n documentation: [\n meta.meaning,\n ``,\n `Format-capable: ${meta.formatCapable}`,\n `Parse-capable: ${meta.parseCapable}`,\n `Locale-sensitive: ${meta.localeSensitive}`,\n `Calendar-sensitive: ${meta.calendarSensitive}`,\n `Timezone-sensitive: ${meta.timezoneSensitive}`,\n `Round-trip safe: ${meta.roundTripSafe}`,\n `Supported types: ${meta.supportedTypes.join(', ')}`,\n ].join('\\n'),\n family: tokenFamily(name),\n };\n });\n}\n\nexport interface TokenHoverDoc {\n signature: string;\n summary: string;\n details: string;\n examples?: string[];\n}\n\nexport function getHoverDocs(): Record<string, TokenHoverDoc> {\n const result: Record<string, TokenHoverDoc> = {};\n for (const name of ALL_TOKEN_NAMES) {\n const meta = TOKEN_METADATA[name]!;\n result[name] = {\n signature: name,\n summary: meta.meaning,\n details: [\n // every entry in TOKEN_METADATA currently has formatCapable:\n // true (verified: 51/51). The 'no' branch reflects real,\n // intended behavior for a future format-incapable token —\n // there's no such token today, so this isn't reachable data,\n // not dead logic. Every other field on this line has real\n // true/false variance across the token table and is exercised\n // by this same loop running over ALL_TOKEN_NAMES.\n /* c8 ignore next */\n `Format-capable: ${meta.formatCapable ? 'yes' : 'no'}`,\n `Parse-capable: ${meta.parseCapable ? 'yes' : 'no'}`,\n `Locale-sensitive: ${meta.localeSensitive ? 'yes' : 'no'}`,\n `Calendar-sensitive: ${meta.calendarSensitive ? 'yes' : 'no'}`,\n `Timezone-sensitive: ${meta.timezoneSensitive ? 'yes' : 'no'}`,\n `Round-trip safe: ${meta.roundTripSafe ? 'yes' : 'no'}`,\n `Supported types: ${meta.supportedTypes.join(', ')}`,\n ].join('\\n'),\n };\n }\n return result;\n}\n\n// Inline diagnostic data for a format string. Returns a list of issues\n// (warnings + errors) with position + message + suggested fix where\n// available. Mirrors analyzeFormat's warnings but adds the position\n// info an editor needs to draw squigglies.\nexport interface InlineDiagnostic {\n startColumn: number;\n endColumn: number;\n severity: 'error' | 'warning' | 'info';\n message: string;\n code: string;\n // Suggested replacement (for quick-fix). Empty when no quick fix exists.\n suggestion?: string;\n}\n\nexport function getInlineDiagnostics(formatStr: string): InlineDiagnostic[] {\n const analysis = analyzeFormat(formatStr);\n const diagnostics: InlineDiagnostic[] = [];\n for (const warning of analysis.warnings) {\n // Find the token that triggered the warning. analyzeFormat doesn't\n // currently surface which token caused which warning — we'd need\n // to extend it to track per-warning source positions. For now,\n // surface as a whole-string diagnostic.\n let message = warning.message;\n let suggestion: string | undefined;\n if (warning.code === 'TWELVE_HOUR_WITHOUT_A') {\n suggestion = 'Add an \"a\" token (e.g. \"h:mm a\") or use \"H:mm\" for 24-hour.';\n } else if (warning.code === 'MIXED_12_AND_24_HOUR') {\n suggestion = 'Pick one form: \"HH:mm\" for 24-hour or \"h:mm a\" for 12-hour.';\n } else if (warning.code === 'AMBIGUOUS_NUMERIC_RUN') {\n suggestion = 'Add a separator or use padded forms (e.g. \"MM\" instead of \"M\").';\n } else if (warning.code === 'FORMAT_ONLY_TOKEN') {\n suggestion = 'Use a parse-capable variant (e.g. \"d\" instead of \"do\").';\n }\n diagnostics.push({\n startColumn: 0,\n endColumn: formatStr.length,\n severity: 'warning',\n message,\n code: warning.code,\n suggestion,\n });\n }\n return diagnostics;\n}\n\n// Day.js / date-fns token conversion hints. Used by the IDE to suggest\n// the temporal-fmt equivalent when a user types a Day.js or date-fns\n// token. Pulls the translation tables from the codemod if available;\n// falls back to inline tables otherwise.\nexport interface TokenConversionHint {\n from: string;\n // null means \"real token in the source library, no temporal-fmt\n // equivalent\" — the codemod throws on these rather than treating\n // them as unrecognized text (see codemod.ts).\n to: string | null;\n notes?: string;\n}\n\n// Day.js → temporal-fmt mappings. Day.js uses Moment-style tokens:\n// `D`/`DD` are day-of-*month* here (unlike date-fns below, where the\n// same letters mean day-of-year). Assumes the AdvancedFormat plugin\n// for `Do`/`Q`/`k`/`kk`/`X`/`x` — see codemod.ts's doc comment for why.\nexport const DAYJS_TO_TEMPORAL_FMT: TokenConversionHint[] = [\n { from: 'YYYY', to: 'yyyy' },\n { from: 'YY', to: 'yy' },\n { from: 'MMMM', to: 'MMMM' },\n { from: 'MMM', to: 'MMM' },\n { from: 'MM', to: 'MM' },\n { from: 'M', to: 'M' },\n { from: 'DD', to: 'dd' },\n { from: 'D', to: 'd' },\n { from: 'Do', to: null, notes: 'Ordinal day (AdvancedFormat) — use temporal-fmt\\'s own \"do\" token instead.' },\n { from: 'dddd', to: 'EEEE' },\n { from: 'ddd', to: 'EEE' },\n { from: 'dd', to: null, notes: 'Min-name weekday (e.g. \"Tu\") — temporal-fmt has no equivalent width.' },\n { from: 'd', to: null, notes: 'Numeric weekday (0-6) — no temporal-fmt equivalent; not the same as \"d\" here, which is day-of-month.' },\n { from: 'HH', to: 'HH' },\n { from: 'H', to: 'H' },\n { from: 'hh', to: 'hh' },\n { from: 'h', to: 'h' },\n { from: 'kk', to: null, notes: 'Hour 1-24 (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'k', to: null, notes: 'Hour 1-24 (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'mm', to: 'mm' },\n { from: 'm', to: 'm' },\n { from: 'ss', to: 'ss' },\n { from: 's', to: 's' },\n { from: 'SSS', to: 'SSS' },\n { from: 'A', to: 'a', notes: 'Uppercase AM/PM in Day.js — temporal-fmt is always lowercase.' },\n { from: 'a', to: 'a' },\n { from: 'ZZ', to: 'XX', notes: 'Numeric UTC offset, no colon.' },\n { from: 'Z', to: 'XXX', notes: 'Numeric UTC offset with colon.' },\n { from: 'X', to: null, notes: 'Unix timestamp (seconds) — use fromUnixSeconds() instead.' },\n { from: 'x', to: null, notes: 'Unix timestamp (ms) — use fromUnixMilliseconds() instead.' },\n { from: 'Qo', to: null, notes: 'Ordinal quarter (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'Q', to: 'Q' },\n { from: 'Mo', to: null, notes: 'Ordinal month (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'ww', to: 'ww' },\n { from: 'wo', to: null, notes: 'Ordinal ISO week (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'w', to: null, notes: 'Unpadded ISO week — temporal-fmt\\'s \"ww\" is always 2-digit.' },\n { from: 'gggg', to: 'RRRR', notes: 'Week-numbering year, not the calendar year — same caveat as temporal-fmt\\'s RRRR.' },\n { from: 'L', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LLL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LLLL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LT', to: null, notes: 'Localized time format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LTS', to: null, notes: 'Localized time format (AdvancedFormat) — write the format string out explicitly.' },\n];\n\n// date-fns → temporal-fmt mappings. date-fns already uses Unicode/LDML\n// -style tokens close to temporal-fmt's own vocabulary — most strings\n// pass through unchanged. The catch: `D`/`DD` here mean day-of-*year*\n// (same as temporal-fmt's `D`/`DD`), not day-of-month like Day.js's\n// `D`/`DD` above — don't reuse the Day.js table for this, they collide.\nexport const DATE_FNS_TO_TEMPORAL_FMT: TokenConversionHint[] = [\n { from: 'yyyy', to: 'yyyy' },\n { from: 'yy', to: 'yy' },\n { from: 'y', to: null, notes: 'Unpadded calendar year, opt-in via useAdditionalWeekYearTokens — temporal-fmt has no unpadded-year token; use \"yyyy\".' },\n { from: 'MMMM', to: 'MMMM' },\n { from: 'MMM', to: 'MMM' },\n { from: 'MM', to: 'MM' },\n { from: 'M', to: 'M' },\n { from: 'LLLL', to: 'LLLL' },\n { from: 'LLL', to: 'LLL' },\n { from: 'dd', to: 'dd' },\n { from: 'd', to: 'd' },\n { from: 'do', to: 'do' },\n { from: 'DDD', to: 'DDD' },\n { from: 'DD', to: 'DD' },\n { from: 'D', to: 'D', notes: 'Day-of-year, opt-in via useAdditionalDayOfYearTokens — matches temporal-fmt\\'s own \"D\" already.' },\n { from: 'EEEE', to: 'EEEE' },\n { from: 'EEE', to: 'EEE' },\n { from: 'eeee', to: null, notes: 'Locale-aware numeric weekday — no temporal-fmt equivalent.' },\n { from: 'cccc', to: 'cccc' },\n { from: 'ccc', to: 'ccc' },\n { from: 'HH', to: 'HH' },\n { from: 'H', to: 'H' },\n { from: 'hh', to: 'hh' },\n { from: 'h', to: 'h' },\n { from: 'mm', to: 'mm' },\n { from: 'm', to: 'm' },\n { from: 'ss', to: 'ss' },\n { from: 's', to: 's' },\n { from: 'SSS', to: 'SSS' },\n { from: 'a', to: 'a' },\n { from: 'aaa', to: 'a' },\n { from: 'XXX', to: 'XXX' },\n { from: 'XX', to: 'XX' },\n { from: 'X', to: 'X', notes: 'Opt-in via useAdditionalDayOfYearTokens-adjacent rules in date-fns v3+; matches temporal-fmt\\'s own \"X\" already.' },\n { from: 'xxx', to: 'xxx' },\n { from: 'xx', to: 'xx' },\n { from: 'x', to: 'x' },\n { from: 'zzzz', to: 'zzzz' },\n { from: 'zzz', to: 'zzz' },\n { from: 'z', to: 'z' },\n { from: 'QQQ', to: 'QQQ' },\n { from: 'Q', to: 'Q' },\n { from: 'GGGG', to: 'GGGG' },\n { from: 'GGG', to: null, notes: 'Abbreviated era — temporal-fmt only has \"G\" (short) and \"GGGG\" (long).' },\n { from: 'GG', to: null, notes: 'Abbreviated era — temporal-fmt only has \"G\" (short) and \"GGGG\" (long).' },\n { from: 'G', to: 'G' },\n { from: 'ww', to: 'ww' },\n { from: 'w', to: null, notes: 'Unpadded local week number — temporal-fmt\\'s \"ww\" is ISO-week and always 2-digit.' },\n { from: 'RRRR', to: 'RRRR' },\n { from: 'R', to: null, notes: 'Unpadded ISO week-numbering year — temporal-fmt\\'s \"RRRR\" is always 4-digit.' },\n { from: 'Y', to: null, notes: 'Week-numbering year (locale week rules), opt-in — no temporal-fmt equivalent; \"RRRR\" is ISO week-numbering, a different rule set.' },\n { from: 'PPPP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'PPP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'PP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'P', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'pppp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'ppp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'pp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'p', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n];\n\n// Format preview — for a given format string and a sample Temporal\n// value, returns the formatted string. Editor plugins use this for\n// live preview as the user types.\nexport function previewFormat(formatStr: string, sample?: unknown): string {\n // Use a fixed sample date if none provided — 2026-08-04T15:45:30.\n // Picked because it exercises every common token: weekday is Tuesday,\n // month name has 5 chars, day is single-digit, hour is 12-hour-style,\n // minute/second both pad.\n const value = sample ?? {\n year: 2026, month: 8, day: 4, hour: 15, minute: 45, second: 30, millisecond: 123,\n dayOfWeek: 2, timeZoneId: 'UTC', offset: '+00:00', calendarId: 'iso8601',\n };\n return builtinFormat(value as Parameters<typeof builtinFormat>[0], formatStr, { locale: 'en-US' });\n}\n\n// Documentation links. Docs used to live under docs/ as one file per\n// token family; they're now consolidated into the root README's \"Token\n// reference\" section, which has no per-token anchors — so this points\n// at the section instead of a token-specific fragment.\nexport function getDocUrl(tokenName: string): string {\n void tokenName;\n return 'README.md#token-reference';\n}\n\n// Re-exports for callers that want everything IDE-related from one module.\nexport { listTokens, TOKEN_METADATA, ALL_TOKEN_NAMES };","// Day.js / date-fns format-string translation — backs the CLI's\n// `translate` subcommand. The mapping tables already live in\n// ideData.ts for the IDE tooling, so this module builds the actual\n// string translator on top of them rather than depending on anything\n// external.\n//\n// Scope: format *strings* only (`\"YYYY-MM-DD\"` -> `\"yyyy-MM-dd\"`), not\n// an AST codemod that rewrites call sites. Rewriting\n// `dayjs(x).format(fmt)` call expressions across a codebase is a\n// separate, bigger job — see `temporal-fmt-codemod` for that.\n\nimport { DAYJS_TO_TEMPORAL_FMT, DATE_FNS_TO_TEMPORAL_FMT, type TokenConversionHint } from './ideData.js';\n\ninterface SourcePiece {\n kind: 'token' | 'literal';\n value: string;\n}\n\n// Day.js and date-fns both escape literal text by wrapping it in\n// square brackets (`[MM]` stays literal \"MM\"), unlike temporal-fmt's\n// single-quote escaping. Splitting on that first keeps the token scan\n// below from matching inside an escaped span.\nfunction splitOnBrackets(source: string): SourcePiece[] {\n const pieces: SourcePiece[] = [];\n let i = 0;\n while (i < source.length) {\n if (source[i] === '[') {\n const end = source.indexOf(']', i + 1);\n if (end === -1) {\n pieces.push({ kind: 'token', value: source.slice(i) });\n break;\n }\n pieces.push({ kind: 'literal', value: source.slice(i + 1, end) });\n i = end + 1;\n } else {\n let j = i;\n while (j < source.length && source[j] !== '[') j += 1;\n pieces.push({ kind: 'token', value: source.slice(i, j) });\n i = j;\n }\n }\n return pieces;\n}\n\n// Splits a \"token\" span (the non-bracketed parts of the source string)\n// into runs of identical letters and everything else, greedy-longest\n// against the known token table first — same overall approach as\n// tokenize.ts, but here we're scanning the *source* library's\n// vocabulary rather than temporal-fmt's own.\nfunction splitIntoRuns(span: string, sortedFrom: string[]): SourcePiece[] {\n const pieces: SourcePiece[] = [];\n let i = 0;\n while (i < span.length) {\n const match = sortedFrom.find((tok) => span.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += span[i];\n } else {\n pieces.push({ kind: 'literal', value: span[i] });\n }\n i += 1;\n }\n return pieces;\n}\n\nfunction translate(source: string, table: TokenConversionHint[], sourceLibLabel: string): string {\n const byFrom = new Map(table.map((hint) => [hint.from, hint.to]));\n const sortedFrom = [...byFrom.keys()].sort((a, b) => b.length - a.length);\n\n const bracketPieces = splitOnBrackets(source);\n let out = '';\n for (const piece of bracketPieces) {\n if (piece.kind === 'literal') {\n // Re-escape for temporal-fmt's quote syntax; a literal quote\n // character inside the escaped span needs doubling.\n out += `'${piece.value.replace(/'/g, \"''\")}'`;\n continue;\n }\n for (const run of splitIntoRuns(piece.value, sortedFrom)) {\n if (run.kind === 'token') {\n const mapped = byFrom.get(run.value);\n if (mapped === undefined || mapped === null) {\n throw new Error(\n `temporal-fmt: \"${run.value}\" has no ${sourceLibLabel} -> temporal-fmt mapping ` +\n `(in format string \"${source}\"). Write the equivalent temporal-fmt token in by hand, ` +\n `or wrap it in brackets if it was meant as literal text.`\n );\n }\n out += mapped;\n } else if (/[a-zA-Z]/.test(run.value)) {\n // Unrecognized letters can't pass through unescaped — temporal-fmt\n // would read them as its own tokens. Quote the whole run.\n out += `'${run.value.replace(/'/g, \"''\")}'`;\n } else {\n out += run.value;\n }\n }\n }\n return out;\n}\n\n/**\n * Translates a Day.js format string to the equivalent temporal-fmt\n * token string.\n *\n * ```js\n * translateDayjsFormatString('YYYY-MM-DD HH:mm:ss'); // \"yyyy-MM-dd HH:mm:ss\"\n * translateDayjsFormatString('[Q]Q YYYY'); // \"'Q'Q yyyy\" (bracketed text stays literal)\n * ```\n *\n * Throws if the format string uses a Day.js token with no temporal-fmt\n * equivalent (`Do`, `Mo`, `Qo`, `k`, `kk`, `X`, `x`, the localized\n * `L`-family tokens) — these need to be rewritten by hand, since\n * there's no single temporal-fmt token that means the same thing.\n *\n * Note: `Do`/`Mo`/`Qo`/`k`/`kk`/`X`/`x` only have meaning in Day.js\n * once the `AdvancedFormat` (and, for `X`/`x`, base) plugins are\n * loaded — without them Day.js silently glues the parts together or\n * passes the letters through unrendered instead of erroring. This\n * function assumes the plugin is loaded, since that's the common case\n * for anyone who put these tokens in a format string on purpose.\n */\nexport function translateDayjsFormatString(formatStr: string): string {\n return translate(formatStr, DAYJS_TO_TEMPORAL_FMT, 'Day.js');\n}\n\n/**\n * Translates a date-fns format string to the equivalent temporal-fmt\n * token string. date-fns already uses Unicode-style tokens close to\n * temporal-fmt's own, so most strings pass through with only a case\n * change on the weekday tokens; the notable divergences are date-fns's\n * `D`/`DD` (day-of-year, matches temporal-fmt already) versus its `d`/\n * `dd` (day-of-month, also matches already) and the locale-dependent\n * `P`/`p` composite tokens, which have no single temporal-fmt token\n * and throw.\n */\nexport function translateDateFnsFormatString(formatStr: string): string {\n return translate(formatStr, DATE_FNS_TO_TEMPORAL_FMT, 'date-fns');\n}\n"],"mappings":"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","names":["index_exports","__export","ALL_TOKEN_NAMES","AmbiguousInputError","DATE_FNS_TO_TEMPORAL_FMT","DAYJS_TO_TEMPORAL_FMT","DEFAULT_CONFIG","FORMAT_ONLY_TOKENS","FormatSyntaxError","InvalidCalendarError","InvalidDateError","InvalidDurationError","InvalidLocaleError","InvalidOffsetError","InvalidTimeError","InvalidTimeZoneError","ParseMismatchError","SUPPORTED_NUMBERING_SYSTEMS","TOKEN_METADATA","TemporalFmtError","UnknownTokenError","add","addBusinessDays","addDays","addDuration","addHours","addMilliseconds","addMinutes","addMonths","addSeconds","addWeeks","addYears","analyzeFormat","applyNumbering","applyParseNumbering","asDateFieldView","assertDuration","assertInstant","assertPlainDate","assertPlainDateTime","assertPlainMonthDay","assertPlainTime","assertPlainYearMonth","assertTemporal","assertZonedDateTime","balanceDuration","between","ceil","clamp","compare","compareDuration","compileFormat","compileParser","convertDigits","convertDigitsToAscii","createBusinessCalendar","createConfig","createFormatter","createHolidayCalendar","dayOfYearHelper","daysInMonth","daysInYear","difference","differenceInBusinessDays","differenceInDays","differenceInHours","differenceInMilliseconds","differenceInMinutes","differenceInMonths","differenceInSeconds","differenceInWeeks","differenceInYears","endOf","explainFormat","fieldForToken","floor","format","formatDistance","formatDistanceToNow","formatDuration","formatDurationToParts","formatHTTPDate","formatISO","formatISODuration","formatRFC2822","formatRFC3339","formatRRule","formatRange","formatRangeToParts","formatRelative","formatRelativeToNow","formatSQL","formatToParts","fromUnixMicroseconds","fromUnixMilliseconds","fromUnixNanoseconds","fromUnixSeconds","getAutocompleteData","getDocUrl","getHoverDocs","getInlineDiagnostics","getLocale","getMonth","getNextTransition","getOffset","getOffsetNanoseconds","getPreviousTransition","getQuarter","getTimeZone","getTransitions","getWeekday","hasLocale","holidaysBetween","intersection","intersects","interval","contains","isAfter","isBefore","subtract","isBetween","isBusinessDay","isDST","isDuration","isEqual","isInstant","isLeapMonth","isLeapYear","isPlainDate","isPlainDateTime","isPlainMonthDay","isPlainTime","isPlainYearMonth","isSameDay","isSameMonth","isSameQuarter","isSameWeek","isSameYear","isTemporal","isToday","isTomorrow","isValidFormat","isWeekday","isWeekend","isYesterday","isZonedDateTime","listRegisteredGrammars","listTokens","max","mergeIntervals","mergeWithConfig","min","monthsInYear","nextBusinessDay","nextHoliday","overlaps","parse","parseDuration","parseHTTPDate","parseISO","parseISODuration","parseRFC2822","parseRFC3339","parseRRule","parseRelative","parseSQL","parseToParts","possibleInstantsFor","previewFormat","previousBusinessDay","previousHoliday","recurrence","registerLocale","registerLocaleVocab","registerRelativeGrammar","resolveZoned","round","roundDuration","safeParse","setTemporal","skip","splitInterval","startOf","subtractBusinessDays","subtractDays","subtractDuration","subtractHours","subtractMilliseconds","subtractMinutes","subtractMonths","subtractSeconds","subtractWeeks","subtractYears","take","toUnixMicroseconds","toUnixMilliseconds","toUnixNanoseconds","toUnixSeconds","tokenInfo","tokenizeFormat","totalDuration","translateDateFnsFormatString","translateDayjsFormatString","truncate","tryParse","union","validateFormat","weekOfYear","weekYear","__toCommonJS","injectedTemporal","onTemporalChanged","subscribeToTemporalChanges","listener","setTemporal","temporal","resolveTemporal","getTemporal","TemporalFmtError","message","fields","capture","FormatSyntaxError","rest","UnknownTokenError","ParseMismatchError","InvalidDateError","InvalidTimeError","InvalidOffsetError","InvalidTimeZoneError","InvalidCalendarError","AmbiguousInputError","InvalidLocaleError","InvalidDurationError","wrapUntypedError","err","context","msg","lowerMsg","customVocabs","MAX_CUSTOM_VOCABS","MAX_LOCALE_TAG_LENGTH","MAX_VOCAB_ENTRY_LENGTH","assertValidVocab","vocab","locale","required","key","length","label","value","entry","assertNoCollision","onVocabChanged","subscribeToVocabChanges","listener","registerLocaleVocab","cacheKey","canonicalCacheKey","vocabCache","normalizeLocaleTag","canonicalKeyCache","MAX_CANONICAL_KEY_CACHE","hit","oldestKey","assertValidLocaleTag","InvalidLocaleError","MAX_VOCAB_CACHE_SIZE","partValue","formatter","date","type","parts","index","p","prev","next","names","seen","i","prior","getCustomVocab","getLocaleVocab","custom","cached","intlLocale","monthLongFmt","monthShortFmt","monthLong","monthShort","m","weekdayLongFmt","weekdayShortFmt","weekdayLong","weekdayShort","d","dayPeriodFmt","am","pm","dayPeriod","CUMULATIVE_DAYS_BY_MONTH","isGregorianLeapYear","year","daysInYear","dayOfYear","month","day","doy","REFERENCE_YEAR","dayOfWeekOfJan1","year","offset","REFERENCE_YEAR","y","daysInYear","isoWeekYearAndWeek","month","day","dayOfWeek","thursdayDoyRelative","dayOfYear","isoYear","thursdayDoy","firstThursdayDoy","week","pad","n","len","negative","digits","t","width","nanoOfSecond","DEFAULT_LOCALE","formatterCache","MAX_CACHE_SIZE","getFormatter","locale","options","key","canonicalCacheKey","formatter","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","nativeSupport","subscribeToTemporalChanges","intlSupportsNativeTemporal","temporal","getTemporal","intlPart","partType","calendar","formatterOptions","ls","toInstant","timeZoneId","isZoned","intlSafeTemporal","nativeOptions","parts","index","p","value","prev","next","dayPeriodPart","hour","custom","getCustomVocab","date","part","localeAwareName","customArray","customIndex","formatOffset","offset","variant","sign","hours","minutes","TOKENS","day","lastDigit","lastTwoDigits","week","isoWeekYearAndWeek","isoYear","dayOfYear","SORTED_TOKEN_STRINGS","TOKENS","tok","a","b","tokenize","format","pieces","i","ch","appendLiteral","j","literal","closed","FormatSyntaxError","match","runChar","end","UnknownTokenError","value","last","SUPPORTED_NUMBERING_SYSTEMS","digitMapCache","getDigitMap","system","map","i","fmt","convertDigits","s","InvalidLocaleError","result","ch","convertDigitsToAscii","reverse","k","applyNumbering","options","applyParseNumbering","HANDLER_BY_TOKEN","TOKENS","tok","fn","field","tokenizeCache","MAX_TOKENIZE_CACHE_SIZE","getPieces","formatStr","pieces","oldestKey","tokenize","format","temporal","options","FormatSyntaxError","locale","DEFAULT_LOCALE","result","piece","handler","UnknownTokenError","applyNumbering","formatToParts","last","compileFormat","out","_handlerFor","token","HANDLER_BY_TOKEN","escapeRegExp","literal","alternation","values","caseInsensitive","escaped","foldCase","value","ch","TIME_ZONE_SHAPE","OFFSET_SHAPES","getTimeZoneFragment","validZoneSet","getValidZoneSet","FIXED_OFFSET_RE","isValidTimeZone","raw","NUMERIC_FRAGMENTS","QQQ_FRAGMENT","FORMAT_ONLY_TOKENS","YYYY_EXACT","YYYY_EXTENDED","DIGIT_LEADING_TOKENS","tokenFragment","token","locale","nextToken","numeric","UnknownTokenError","vocab","getLocaleVocab","UNPADDED_NUMERIC_TOKENS","UNPADDED_NUMERIC_RANGES","enumerateValidSplits","digits","tokens","memo","solve","tokenIndex","offset","key","cached","result","ranges","results","width","min","max","piece","restSplit","escapeRegExp","literal","MAX_AMBIGUITY_BITS","widthChoicesBits","choices","isDigitConsumingStart","piece","DIGIT_LEADING_TOKENS","buildCapturingPattern","pieces","locale","groups","ambiguousRuns","source","i","ambiguityBits","currentRun","flushRun","nextPiece","name","token","tokenFragment","runName","tokenCount","idx","UNPADDED_NUMERIC_TOKENS","nextToken","FormatSyntaxError","patternCache","MAX_CACHE_SIZE","subscribeToVocabChanges","getPattern","formatStr","locale","key","canonicalCacheKey","pattern","oldestKey","buildCapturingPattern","tokenize","calendarCache","MAX_CALENDAR_CACHE_SIZE","resolveCalendar","assertValidLocaleTag","canonicalLocale","calendar","parts","extensionIndex","calendarKeyIndex","resolved","parseOffsetString","raw","token","sign","body","hoursStr","minutesStr","hours","minutes","InvalidOffsetError","assignField","fields","value","applyGroup","vocab","getLocaleVocab","nanoOfSecond","periodIndex","p","pickLenientSplit","splits","tokens","dayIndex","smallDaySplits","s","resolveYear","FormatSyntaxError","resolveHour","expected","parse","input","options","applyParseNumbering","DEFAULT_LOCALE","match","ParseMismatchError","name","isValidTimeZone","InvalidTimeZoneError","runValues","run","runDigits","enumerateValidSplits","AmbiguousInputError","picked","idx","year","hour","month","day","minute","second","millisecond","microsecond","nanosecond","timeZoneId","offsetString","weekdayExpected","weekdayRaw","quarter","hasAnyDatePart","hasFullDate","InvalidDateError","hasTime","temporal","getTemporal","timeFields","calendarField","reject","result","zoneOptions","zdt","actualOffset","err","actual","expectedQuarter","safeParse","TemporalFmtError","wrapUntypedError","tryParse","parseToParts","groupIndices","consumed","fromIndices","position","compileParser","opts","DURATION_UNITS","DURATION_TOKEN_STRINGS","single","a","b","tokenizeDuration","format","pieces","i","ch","appendLiteral","j","literal","closed","match","tok","unit","form","value","last","readUnit","duration","field","coerced","unitFormatterCache","MAX_UNIT_FORMATTER_CACHE_SIZE","getUnitFormatter","locale","intlUnit","unitDisplay","key","canonicalCacheKey","cached","oldestKey","formatter","normalizeLocaleTag","err","InvalidLocaleError","formatDuration","formatStr","options","renderDurationPieces","p","showZeroes","useIntl","out","piece","readFields","value","label","obj","hasYear","hasMonth","hasDay","REFERENCE_YEAR","MS_PER_SECOND","MS_PER_MINUTE","MS_PER_HOUR","MS_PER_DAY","daysSinceReference","year","month","day","days","y","isGregorianLeapYear","dayOfYear","toEpochMs","fields","hour","minute","second","millisecond","DEFAULT_CUTOFFS","resolveCutoffs","user","merged","key","secondsMs","minutesMs","hoursMs","daysMs","monthsMs","rtfCache","MAX_RTF_CACHE_SIZE","getRtf","locale","numeric","canonicalLocaleKey","rtf","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","formatDistance","date1","date2","options","fields1","fields2","diffMs","absMs","unitCutoffs","unit","maxMs","candidateUnit","divisor","unitToMs","rounded","DEFAULT_LOCALE","formatDistanceToNow","date","now","nowFields","MAX_REGISTERED_GRAMMARS","registeredGrammars","registerRelativeGrammar","grammar","existingIdx","g","tryRegisteredGrammar","language","input","reference","matcher","match","temporal","getTemporal","fields","listRegisteredGrammars","normalizeForMatch","s","levenshtein","a","b","prev","curr","j","i","cost","FUZZY_MAX_DISTANCE","MAX_RELATIVE_INPUT_LENGTH","MAX_RELATIVE_WORDS","buildEnglishFuzzyVocabulary","ENGLISH_WEEKDAYS","ENGLISH_MONTHS","ENGLISH_MONTH_SHORT","englishFuzzyVocabulary","getEnglishFuzzyVocabulary","fuzzyCorrectEnglish","normalized","vocabulary","words","changed","corrected","word","best","bestDist","candidate","dist","readYear","reference","readMonth","readDay","extractDayOfWeek","dow","nameAlternation","names","n","buildPlainDate","temporal","year","month","day","addDays","days","addUnits","count","unit","refDate","duration","weekdayOffset","rel","refDow","targetDow","diff","resolveToNextOccurrence","refYear","thisYear","err","weekdayIndexFromName","name","idx","monthIndexFromName","longNames","shortNames","longIdx","shortIdx","ENGLISH_GRAMMAR","_m","ctx","m","inPrefix","unitName","hasIn","hasAgo","sign","SPANISH_WEEKDAYS","SPANISH_MONTHS","SPANISH_MONTH_SHORT","SPANISH_GRAMMAR","relWord","weekdayName","unitWord","unitWordToEnglish","_ctx","FRENCH_WEEKDAYS","FRENCH_MONTHS","FRENCH_MONTH_SHORT","FRENCH_GRAMMAR","GERMAN_WEEKDAYS","GERMAN_MONTHS","GERMAN_MONTH_SHORT","GERMAN_GRAMMAR","adj","GRAMMARS","grammarForLocale","locale","lang","languageOf","w","parseRelative","input","referenceDate","options","reference","temporal","getTemporal","trimmed","MAX_RELATIVE_INPUT_LENGTH","normalized","normalizeForMatch","grammar","grammarForLocale","ctx","registered","tryRegisteredGrammar","languageOf","tryMatch","text","matcher","match","NO_MATCH","exactResult","ENGLISH_GRAMMAR","corrected","fuzzyCorrectEnglish","fuzzyResult","isObject","v","guardOrFalse","impl","value","hasMethod","name","hasTag","typeName","isPlainDate","isPlainTime","isPlainDateTime","isZonedDateTime","isInstant","isPlainYearMonth","isPlainMonthDay","isDuration","isTemporal","tag","describeValue","ctor","assertImpl","guard","expectedType","assertTemporal","assertInstant","assertPlainDate","assertPlainTime","assertPlainDateTime","assertZonedDateTime","assertPlainYearMonth","assertPlainMonthDay","assertDuration","TYPES_WITH","TOKEN_METADATA","ALL_TOKEN_NAMES","tokenizeFormat","formatStr","tokenize","listTokens","TOKENS","name","TOKEN_METADATA","tokenInfo","analyzeFormat","pieces","tokens","requiredFields","compatibleTypesSet","parseable","localeSensitive","calendarSensitive","timezoneSensitive","roundTripSafe","warnings","offset","unpaddedRun","flushRun","piece","metadata","handler","_handlerFor","t","stillCompatible","UNPADDED_NUMERIC_TOKENS","tokenNames","has12Hour","has24Hour","hasAPeriod","hasOffset","hasZzz","w","explainFormat","analysis","lines","isValidFormat","validateFormat","fieldForToken","token","requireFields","view","fields","f","daysInMonth","year","month","LENGTHS","isGregorianLeapYear","daysInYear","monthsInYear","_view","isLeapYear","isLeapMonth","dayOfYearHelper","dayOfYear","weekOfYear","isoWeekYearAndWeek","weekYear","validateStartMonth","startMonth","getQuarter","options","shifted","getMonth","getWeekday","touchesTime","unit","recomputeDayOfWeek","jsDow","startOf","value","result","asDateFieldView","endOf","v","out","asDateTime","value","asDateFieldView","recomputeDayOfWeek","view","jsDow","add","amount","unit","result","maxDay","daysInMonth","total","shiftDays","shiftTime","v","days","y","m","d","y2","era","yoe","m2","doy","doe","z","era2","doe2","yoe2","y2out","doy2","mp","d2","m2out","yOut","msDelta","totalMs","dayOverflow","withinDay","hour","minute","second","millisecond","subtract","addYears","addMonths","addWeeks","addDays","addHours","addMinutes","addSeconds","addMilliseconds","subtractYears","subtractMonths","subtractWeeks","subtractDays","subtractHours","subtractMinutes","subtractSeconds","subtractMilliseconds","difference","a","b","av","bv","aDays","toDayCount","MS_PER_HOUR","MS_PER_MINUTE","MS_PER_SECOND","aMs","toTotalMs","diffMs","differenceInYears","differenceInMonths","differenceInWeeks","differenceInDays","differenceInHours","differenceInMinutes","differenceInSeconds","differenceInMilliseconds","toComparableMs","view","y","m","d","y2","era","yoe","m2","doy","doe","asDateTimeFieldView","value","asDateFieldView","compare","a","b","av","bv","am","bm","isEqual","isBefore","isAfter","min","values","best","i","max","clamp","lo","hi","isBetween","readYearMonthDay","v","todayYearMonthDay","now","dateEqualYearMonthDay","isSameDay","reference","isToday","tomorrowYearMonthDay","yesterdayYearMonthDay","isTomorrow","isYesterday","sameWeek","yearA","monthA","dayA","yearB","monthB","dayB","daysSince","weekdayOf","days","jsDow","wa","wb","isSameWeek","isSameMonth","isSameQuarter","isSameYear","isWeekend","isWeekday","MS_PER_UNIT","applyMode","ms","mode","increment","sign","stepped","rounded","toMs","v","y","m","d","y2","era","yoe","m2","doy","doe","fromMs","base","MS_PER_DAY","totalDays","withinDay","hour","minute","second","millisecond","z","mp","year","round","value","options","base","asDateFieldView","mode","increment","MS_PER_UNIT","ms","toMs","rounded","applyMode","fromMs","floor","unit","roundingIncrement","ceil","truncate","fieldToNs","v","nsPer","field","scaled","InvalidDurationError","DURATION_UNIT_TO_NS","isCalendarBound","roundDuration","duration","totalNs","u","stepNs","sign","abs","stepped","remainder","newTotalNs","result","remaining","unitsInOrder","startIdx","i","unitNs","count","parseISO","input","temporal","getTemporal","iso","offsetMatch","err","InvalidDateError","formatISO","value","v","FormatSyntaxError","parseRFC3339","formatRFC3339","s","parseRFC2822","ms","requireInstant","formatRFC2822","inst","formatRFC2822FromMs","parts","get","t","p","tzMatch","tz","sign","hours","minutes","hour","d","days","months","wd","dd","mon","yyyy","hh","mm","ss","parseHTTPDate","trimmed","formatHTTPDate","instant","getInstant","toUnixSeconds","fromUnixSeconds","seconds","fromUnixMilliseconds","fromUnixMicroseconds","µs","fromUnixNanoseconds","ns","toUnixMilliseconds","toUnixMicroseconds","epochNanoseconds","toUnixNanoseconds","parseSQL","parse","formatSQL","format","formatDurationToParts","duration","formatStr","options","renderDurationPieces","p","parseISODuration","input","m","InvalidDurationError","g","toNum","s","formatISODuration","parts","years","months","weeks","days","hours","minutes","seconds","tokenizeDurationFormat","DURATION_TOKEN_STRINGS","single","a","b","pieces","i","ch","appendLiteral","j","literal","closed","FormatSyntaxError","match","tok","value","last","parseDuration","input","formatStr","options","tokens","tokenizeDurationFormat","fields","groupSpecs","regex","tok","unit","form","key","FormatSyntaxError","m","spec","i","raw","value","InvalidDurationError","balanceDuration","duration","NS_PER","totalNs","k","nsPer","v","fieldToNs","result","remaining","count","totalDuration","divisor","compareDuration","a","b","aNs","totalDurationNs","bNs","d","total","addDuration","keys","subtractDuration","negateDuration","rtfCache","MAX_RTF_CACHE_SIZE","getRtf","locale","numeric","key","rtf","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","formatRelative","date1","date2","options","DEFAULT_LOCALE","dayDiff","differenceInDays","absDays","formatRelativeToNow","date","now","nowFields","extendedVocabs","MAX_LOCALE_TAG_LENGTH","MAX_VOCAB_STRING_LENGTH","canonicalKey","locale","registerLocale","vocab","InvalidLocaleError","registerLocaleVocab","validateExtended","key","checkArray","val","length","i","getLocale","ext","getLocaleVocab","hasLocale","DEFAULT_CONFIG","DEFAULT_LOCALE","createConfig","overrides","merged","mergeWithConfig","config","perCall","result","interval","start","end","bounds","compare","contains","iv","value","startCmp","endCmp","afterStart","beforeEnd","overlaps","a","b","intersects","isBefore","isAfter","intersection","startBoundsOpen","endBoundsOpen","union","difference","result","aStartIncluded","aEndIncluded","subtract","mergeIntervals","intervals","sorted","i","current","last","MAX_DATE_MS","splitInterval","startFields","asDateFieldView","endFields","startMs","toMs","endMs","step","sliceStart","sliceEnd","fromMs","v","y","m","d","y2","era","yoe","m2","doy","doe","ms","base","dayOverflow","withinDay","hour","minute","second","millisecond","newMs","formatRange","formatStr","options","startStr","format","endStr","err","fmt","normalizeLocaleTag","startDate","toJSDate","endDate","formatRangeToParts","startParts","formatToParts","endParts","recurrence","start","rule","current","count","history","MAX_HISTORY","atEnd","atStart","matches","value","v","d","compare","advance","steps","unit","add","recordHistory","nextMatch","candidate","safetyCounter","advanced","take","iter","result","i","r","MAX_SKIP_COLLECTION","skip","between","rangeStart","rangeEnd","parseRRule","input","parts","frequencies","part","eq","key","interval","wd","values","day","month","formatRRule","map","createBusinessCalendar","options","weekend","workingHours","i","isBusinessDay","cal","value","v","dow","jsDow","nextBusinessDay","candidate","add","previousBusinessDay","MAX_BUSINESS_DAY_TRAVERSAL","assertBusinessDayCount","days","compareDateFields","a","b","av","bv","ay","by","addBusinessDays","sign","absDays","subtractBusinessDays","differenceInBusinessDays","rawDirection","direction","count","traversedDays","MAX_HOLIDAY_YEAR_RANGE","createHolidayCalendar","specs","cache","forYear","year","list","s","md","value","v","h","start","end","sv","ev","result","y","date","compare","nextHoliday","cal","candidate","add","i","previousHoliday","holidaysBetween","resolveZoned","fields","timeZone","options","temporal","getTemporal","disambiguation","fromOptions","err","msg","getTimeZone","value","v","getOffset","getOffsetNanoseconds","m","isDST","getNextTransition","findTransition","getPreviousTransition","direction","candidateFields","add","startOffset","i","cf","candidateZdt","getTransitions","start","end","result","startV","endV","cursorFields","lastOffset","cursorZdt","possibleInstantsFor","earlier","later","matchesRequestedWallClock","z","createFormatter","options","mergedTokens","TOKENS","customTokenNames","custom","existingIdx","name","a","b","handlerByToken","handler","field","sortedTokenStrings","customTokenize","formatStr","pieces","i","ch","appendLiteral","j","literal","closed","match","tok","runChar","end","UnknownTokenError","value","last","defaultLocale","DEFAULT_LOCALE","temporal","opts","locale","result","piece","out","p","tokenFamily","name","getAutocompleteData","ALL_TOKEN_NAMES","meta","TOKEN_METADATA","getHoverDocs","result","getInlineDiagnostics","formatStr","analysis","analyzeFormat","diagnostics","warning","message","suggestion","DAYJS_TO_TEMPORAL_FMT","DATE_FNS_TO_TEMPORAL_FMT","previewFormat","sample","format","getDocUrl","tokenName","splitOnBrackets","source","pieces","i","end","j","splitIntoRuns","span","sortedFrom","match","tok","last","translate","table","sourceLibLabel","byFrom","hint","a","b","bracketPieces","out","piece","run","mapped","translateDayjsFormatString","formatStr","DAYJS_TO_TEMPORAL_FMT","translateDateFnsFormatString","DATE_FNS_TO_TEMPORAL_FMT"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/temporalProvider.ts","../src/errors.ts","../src/localeVocab.ts","../src/isoWeek.ts","../src/tokens.ts","../src/tokenize.ts","../src/numbering.ts","../src/format.ts","../src/pattern.ts","../src/parsePattern.ts","../src/parse.ts","../src/formatDuration.ts","../src/formatDistance.ts","../src/relativeGrammar.ts","../src/parseRelative.ts","../src/typeGuards.ts","../src/tokenMetadata.ts","../src/analyze.ts","../src/calendarUtils.ts","../src/arithmetic.ts","../src/comparison.ts","../src/rounding.ts","../src/serialization.ts","../src/duration.ts","../src/relativeTime.ts","../src/localeRegistry.ts","../src/config.ts","../src/interval.ts","../src/recurrence.ts","../src/businessCalendar.ts","../src/holidays.ts","../src/timezone.ts","../src/extensibility.ts","../src/ideData.ts","../src/codemod.ts"],"sourcesContent":["export { format, formatToParts, compileFormat, type FormattedPart, type CompiledFormat } from './format.js';\nexport { parse, safeParse, tryParse, parseToParts, compileParser, type SafeParseResult, type ParsedPart, type CompiledParser } from './parse.js';\nexport { setTemporal } from './temporalProvider.js';\nexport { formatDuration } from './formatDuration.js';\nexport { formatDistance, formatDistanceToNow } from './formatDistance.js';\nexport { parseRelative } from './parseRelative.js';\nexport { registerLocaleVocab } from './localeVocab.js';\nexport type { LocaleVocab } from './localeVocab.js';\nexport type { TemporalLike, FormatOptions } from './tokens.js';\nexport type { DurationFormatOptions } from './formatDuration.js';\nexport type { FormatDistanceOptions, DistanceCutoffs } from './formatDistance.js';\nexport type { ParseRelativeOptions } from './parseRelative.js';\nexport type { TemporalNamespace } from './temporalProvider.js';\n\n// type guards\nexport {\n isTemporal, isInstant, isPlainDate, isPlainTime, isPlainDateTime,\n isZonedDateTime, isPlainYearMonth, isPlainMonthDay, isDuration,\n assertTemporal, assertInstant, assertPlainDate, assertPlainTime,\n assertPlainDateTime, assertZonedDateTime, assertPlainYearMonth,\n assertPlainMonthDay, assertDuration,\n} from './typeGuards.js';\n\n// typed errors\nexport {\n TemporalFmtError, FormatSyntaxError, UnknownTokenError, ParseMismatchError,\n InvalidDateError, InvalidTimeError, InvalidOffsetError, InvalidTimeZoneError,\n InvalidCalendarError, AmbiguousInputError, InvalidLocaleError, InvalidDurationError,\n type TemporalFmtErrorFields, type TemporalFmtErrorCode,\n} from './errors.js';\n\n// analyzer / introspection\nexport {\n analyzeFormat, explainFormat, tokenizeFormat, listTokens, tokenInfo,\n isValidFormat, validateFormat, fieldForToken,\n type FormatAnalysis, type AnalyzedToken, type TokenMetadata, type TemporalType,\n} from './analyze.js';\n\n// token metadata (re-exported as a top-level surface for\n// callers like the ESLint plugin that want the table directly without\n// going through analyzeFormat)\nexport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\nexport { FORMAT_ONLY_TOKENS } from './pattern.js';\n\n// calendar utilities\nexport {\n daysInMonth, daysInYear, monthsInYear, isLeapYear, isLeapMonth,\n dayOfYearHelper as dayOfYear, weekOfYear, weekYear,\n getQuarter, getMonth, getWeekday, startOf, endOf, asDateFieldView,\n type DateFieldView, type StartOfUnit, type QuarterOptions,\n} from './calendarUtils.js';\n\n// date arithmetic\nexport {\n add, subtract, difference,\n addYears, addMonths, addWeeks, addDays, addHours, addMinutes, addSeconds, addMilliseconds,\n subtractYears, subtractMonths, subtractWeeks, subtractDays, subtractHours, subtractMinutes, subtractSeconds, subtractMilliseconds,\n differenceInYears, differenceInMonths, differenceInWeeks, differenceInDays, differenceInHours, differenceInMinutes, differenceInSeconds, differenceInMilliseconds,\n type AddUnit, type DiffUnit,\n} from './arithmetic.js';\n\n// comparison\nexport {\n compare, isEqual, isBefore, isAfter, min, max, clamp, isBetween,\n isToday, isTomorrow, isYesterday,\n isSameDay, isSameWeek, isSameMonth, isSameQuarter, isSameYear,\n isWeekend, isWeekday,\n} from './comparison.js';\n\n// rounding\nexport { round, floor, ceil, truncate, roundDuration, type RoundOptions, type DurationFields } from './rounding.js';\n\n// serialization\nexport {\n parseISO, formatISO,\n parseRFC3339, formatRFC3339,\n parseRFC2822, formatRFC2822,\n parseHTTPDate, formatHTTPDate,\n fromUnixSeconds, fromUnixMilliseconds, fromUnixMicroseconds, fromUnixNanoseconds,\n toUnixSeconds, toUnixMilliseconds, toUnixMicroseconds, toUnixNanoseconds,\n parseSQL, formatSQL,\n} from './serialization.js';\n\n// duration (extended)\nexport {\n formatDurationToParts, parseDuration, parseISODuration, formatISODuration,\n balanceDuration, totalDuration, compareDuration, addDuration, subtractDuration,\n} from './duration.js';\nexport { roundDuration as roundDurationAlias } from './rounding.js';\n\n// relative time\nexport { formatRelative, formatRelativeToNow, type FormatRelativeOptions } from './relativeTime.js';\n\n// locale registration\nexport { registerLocale, getLocale, hasLocale, type ExtendedLocaleVocab } from './localeRegistry.js';\n\n// numbering systems\nexport {\n convertDigits, convertDigitsToAscii, applyNumbering, applyParseNumbering,\n SUPPORTED_NUMBERING_SYSTEMS, type NumberingSystem, type NumberingFormatOptions,\n} from './numbering.js';\n\n// config\nexport { createConfig, mergeWithConfig, DEFAULT_CONFIG, type TemporalFmtConfig } from './config.js';\n\n// relative-grammar registration\nexport { registerRelativeGrammar, listRegisteredGrammars, type RelativeGrammar, type RelativeGrammarMatch } from './relativeGrammar.js';\n\n// intervals\nexport {\n interval, contains as intervalContains, overlaps, intersects,\n isBefore as intervalIsBefore, isAfter as intervalIsAfter,\n intersection, union, difference as intervalDifference,\n subtract as intervalSubtract, mergeIntervals, splitInterval,\n formatRange, formatRangeToParts,\n type Interval, type IntervalBounds,\n} from './interval.js';\n\n// recurrence\nexport {\n recurrence, take, skip, between,\n parseRRule, formatRRule,\n type RecurrenceRule, type RecurrenceFrequency, type RecurrenceIterator,\n} from './recurrence.js';\n\n// business calendar\nexport {\n createBusinessCalendar, isBusinessDay, addBusinessDays, subtractBusinessDays,\n differenceInBusinessDays, nextBusinessDay, previousBusinessDay,\n type BusinessCalendar, type BusinessCalendarOptions,\n} from './businessCalendar.js';\n\n// holiday framework\nexport {\n createHolidayCalendar, nextHoliday, previousHoliday, holidaysBetween,\n type HolidayCalendar, type HolidaySpec,\n} from './holidays.js';\n\n// timezone subsystem\nexport {\n resolveZoned, getTimeZone, getOffset, getOffsetNanoseconds, isDST,\n getNextTransition, getPreviousTransition, getTransitions, possibleInstantsFor,\n type DisambiguationMode, type ResolveZonedOptions,\n} from './timezone.js';\n\n// extensibility\nexport {\n createFormatter,\n type Formatter, type FormatterOptions, type CustomToken, type TokenHandler, type TokenField,\n} from './extensibility.js';\n\n// IDE tooling data\nexport {\n getAutocompleteData, getHoverDocs, getInlineDiagnostics,\n previewFormat, getDocUrl,\n DAYJS_TO_TEMPORAL_FMT, DATE_FNS_TO_TEMPORAL_FMT,\n type TokenAutocompleteEntry, type TokenHoverDoc,\n type InlineDiagnostic, type TokenConversionHint,\n} from './ideData.js';\n\n// Day.js / date-fns format-string translation — backs the CLI's\n// `translate` subcommand (see scripts/cli.mjs).\nexport {\n translateDayjsFormatString, translateDateFnsFormatString,\n} from './codemod.js';\n","// This package's tsconfig assumes lib: [\"ESNext\"] only — no ambient\n// `Temporal` namespace type. Everywhere else in this codebase only ever\n// *reads* fields off a Temporal-like object the caller already built\n// (TemporalLike in tokens.ts). This module is the single choke point for\n// touching a *namespace*-shaped Temporal (PlainDate.from, etc.): parse()\n// uses it to construct a result, tokens.ts uses it to probe native\n// Intl<->Temporal support. Consumers can hand us an implementation via\n// setTemporal(), or we fall back to globalThis.Temporal\ninterface TemporalFactory {\n from(fields: Record<string, number | string | undefined>, options?: { overflow?: 'constrain' | 'reject'; disambiguation?: 'compatible' | 'earlier' | 'later' | 'reject'; offset?: 'use' | 'ignore' | 'prefer' | 'reject' }): unknown;\n // compare() lives on the namespace (Temporal.PlainDate.compare), not on\n // instances. Optional here since a consumer might be passing a\n // stripped-down Temporal shim that doesn't expose it.\n compare?(one: unknown, two: unknown): number;\n}\n\n// Instant is constructed via fromEpochMilliseconds / fromEpochNanoseconds\n// rather than from(fields). Separate interface.\ninterface InstantFactory {\n from(iso: string): unknown;\n fromEpochMilliseconds(ms: number): unknown;\n fromEpochMicroseconds(µs: number): unknown;\n fromEpochNanoseconds(ns: bigint): unknown;\n}\n\n// Duration is constructed via from(fields). Carries round() and compare().\ninterface DurationFactory extends TemporalFactory {\n compare?(one: unknown, two: unknown): number;\n prototype?: { round(options: unknown): unknown };\n}\n\n// PlainYearMonth / PlainMonthDay are also constructed via from(fields).\n// Same factory shape, kept separate for type clarity in callers that\n// want to narrow.\nexport interface TemporalNamespace {\n PlainDate: TemporalFactory;\n PlainTime: TemporalFactory;\n PlainDateTime: TemporalFactory;\n ZonedDateTime: TemporalFactory;\n Instant?: InstantFactory;\n Duration?: DurationFactory;\n PlainYearMonth?: TemporalFactory;\n PlainMonthDay?: TemporalFactory;\n}\n\nlet injectedTemporal: TemporalNamespace | undefined;\n\n// Anything that caches a result derived from *which* Temporal\n// implementation is active (right now: tokens.ts's native-Intl-support\n// probe) registers here so it gets invalidated whenever setTemporal()\n// swaps the implementation — see the comment on setTemporal() below for\n// why that matters. A plain array instead of an event-emitter-style API\n// since this only ever needs \"call every listener, in registration order,\n// with no payload\" — nothing here needs unsubscribe or payload data.\nconst onTemporalChanged: Array<() => void> = [];\n\nexport function subscribeToTemporalChanges(listener: () => void): void {\n onTemporalChanged.push(listener);\n}\n\n/**\n * Explicitly hand temporal-fmt the Temporal implementation to use, instead\n * of relying on a global `Temporal`. Call this once, before your first\n * `format()`/`parse()`/`parseISO()`/etc.\n *\n * Call with no argument (or `undefined`) to clear the override and fall\n * back to `globalThis.Temporal` again.\n *\n * @example\n * import { Temporal } from 'temporal-polyfill';\n * import { setTemporal } from 'temporal-fmt';\n * setTemporal(Temporal);\n */\nexport function setTemporal(temporal?: TemporalNamespace): void {\n injectedTemporal = temporal;\n // tokens.ts's native-Intl-support probe is keyed on whichever Temporal\n // implementation was active the first time a locale-aware format() ran —\n // if that implementation changes later (native -> polyfill or back), the\n // memoized probe result can go stale and disagree with what's now\n // actually active. Resetting it here means the next locale-aware format()\n // after any setTemporal() call re-probes against the implementation\n // that's active *now*, at the (small, one-time-per-switch) cost of\n // re-running the probe.\n for (const listener of onTemporalChanged) listener();\n}\n\nfunction resolveTemporal(): TemporalNamespace | undefined {\n return injectedTemporal ?? (globalThis as unknown as { Temporal?: TemporalNamespace }).Temporal;\n}\n\nexport function getTemporal(): TemporalNamespace {\n const temporal = resolveTemporal();\n if (!temporal) {\n throw new Error(\n 'temporal-fmt: parse() needs a Temporal implementation to construct its result. ' +\n 'Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal ' +\n '(native on Node 26+, or a polyfill like temporal-polyfill).'\n );\n }\n return temporal;\n}\n","// Structured error classes for parse()/format() failures.\n//\n// As of 0.9.0, every throw site on the parse/format data path —\n// tokenize.ts, pattern.ts, format.ts, parse.ts (parse/safeParse/tryParse/\n// parseToParts/compileParser), and the two data-path throws in\n// localeVocab.ts (partValue/assertNoCollision on the getLocaleVocab side)\n// — throws these typed classes directly instead of plain\n// `new Error(message)`. Every migrated site kept its exact pre-0.9.0\n// message text — subclasses of TemporalFmtError still satisfy\n// `instanceof Error` and message-matching regexes (e.g.\n// `/token \"HH\" requires/`), so this was safe to do without a semver-major\n// bump for either check. What *is* a breaking change for 0.9.0: code that\n// specifically checked `err.constructor === Error` or `err.name ===\n// 'Error'` will see a different name now (e.g. 'FormatSyntaxError'). See\n// the 0.9.0 changelog entry.\n//\n// Not migrated, on purpose:\n// - localeVocab.ts's registration-time throws (assertValidVocab,\n// registerLocaleVocab itself) — these are config-time API-misuse\n// errors on data the developer supplies once at startup, not runtime\n// parse/format failures, so they don't fit this module's\n// TemporalFmtErrorCode taxonomy as-is. assertNoCollision is shared\n// between the registration path and the data path (getLocaleVocab), so\n// its throw stays plain `Error` for both until that split is done —\n// see the tracking note at its call site.\n//\n// wrapUntypedError() below still exists for localeVocab.ts's registration\n// throws and for anything a caller passes into safeParse()/tryParse() from\n// outside this package. On the data path, safeParse's `if (err instanceof\n// TemporalFmtError) return { ok: false, error: err }` now catches every\n// throw before it would reach the classifier, so the regex branches here\n// are effectively dead for parse/format/tokenize/pattern — left in place\n// as the fallback for anything not yet on a typed throw site.\n\nexport type TemporalFmtErrorCode =\n | 'FORMAT_SYNTAX_ERROR'\n | 'UNKNOWN_TOKEN'\n | 'PARSE_MISMATCH'\n | 'INVALID_DATE'\n | 'INVALID_TIME'\n | 'INVALID_OFFSET'\n | 'INVALID_TIME_ZONE'\n | 'INVALID_CALENDAR'\n | 'AMBIGUOUS_INPUT'\n | 'INVALID_LOCALE'\n | 'INVALID_DURATION';\n\nexport interface TemporalFmtErrorFields {\n code: TemporalFmtErrorCode;\n input?: string;\n format?: string;\n token?: string;\n position?: number;\n expected?: string;\n actual?: string;\n reason?: string;\n}\n\n// Base class. `message` is the human-readable summary; the structured\n// fields are the machine-readable parts a linter or codemod reports on.\n// `Error.captureStackTrace` is called manually (where available) so\n// the stack points at the call site, not at this constructor — same\n// pattern Node uses for its own error classes.\nexport class TemporalFmtError extends Error {\n readonly code: TemporalFmtErrorCode;\n readonly input?: string;\n readonly format?: string;\n readonly token?: string;\n readonly position?: number;\n readonly expected?: string;\n readonly actual?: string;\n readonly reason?: string;\n\n constructor(message: string, fields: TemporalFmtErrorFields) {\n super(message);\n this.name = 'TemporalFmtError';\n this.code = fields.code;\n this.input = fields.input;\n this.format = fields.format;\n this.token = fields.token;\n this.position = fields.position;\n this.expected = fields.expected;\n this.actual = fields.actual;\n this.reason = fields.reason;\n // Avoid setting the stack to point at this constructor — keeps it\n // useful. captureStackTrace is V8-only; on other engines the\n // default stack (this constructor line) is what callers get.\n const capture = (Error as unknown as { captureStackTrace?: (target: Error, ctor?: Function) => void }).captureStackTrace;\n if (typeof capture === 'function') {\n capture(this, this.constructor);\n }\n }\n\n // Lets callers do `err.toJSON()` for logging. Plain `Error` doesn't\n // serialize its non-enumerable fields; this picks them up explicitly.\n toJSON(): TemporalFmtErrorFields & { name: string; message: string } {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n input: this.input,\n format: this.format,\n token: this.token,\n position: this.position,\n expected: this.expected,\n actual: this.actual,\n reason: this.reason,\n };\n }\n}\n\n// Each subclass fixes `code` so callers can switch on it without\n// re-checking message text. The constructor takes only the fields\n// that vary per call; `code` and a default `message` template are\n// provided by the subclass.\n\nexport class FormatSyntaxError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `format string \"${fields.format ?? ''}\" has a syntax error${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'FORMAT_SYNTAX_ERROR', ...rest },\n );\n this.name = 'FormatSyntaxError';\n }\n}\n\nexport class UnknownTokenError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `token \"${fields.token ?? ''}\" is not a recognized temporal-fmt token${fields.format ? ` in format string \"${fields.format}\"` : ''}.`,\n { code: 'UNKNOWN_TOKEN', ...rest },\n );\n this.name = 'UnknownTokenError';\n }\n}\n\nexport class ParseMismatchError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not match format \"${fields.format ?? ''}\"${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'PARSE_MISMATCH', ...rest },\n );\n this.name = 'ParseMismatchError';\n }\n}\n\nexport class InvalidDateError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid date${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DATE', ...rest },\n );\n this.name = 'InvalidDateError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidTimeError is part of the public\n error-class surface (exported from index.ts, code 'INVALID_TIME')\n but nothing in this package constructs one. Investigated wiring it\n in the same way InvalidTimeZoneError was just wired (see parse.ts's\n zzz-validation loop): checked whether hour/minute/second have a\n post-match semantic range check the way zone ids do. They don't —\n pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce\n their valid ranges at the regex level (e.g. HH is '(?:[01]\\d|2[0-3])',\n which can't match \"99\" in the first place), so an out-of-range time\n is rejected as a plain shape mismatch before any semantic check\n could run. There's no live gap to hook this into without inventing a\n redundant check purely to give this class a body. Left unconstructed\n until the library actually has a real invalid-time case to report. */\nexport class InvalidTimeError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" does not describe a valid time${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME', ...rest },\n );\n this.name = 'InvalidTimeError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class InvalidOffsetError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `offset \"${fields.actual ?? ''}\" is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_OFFSET', ...rest },\n );\n this.name = 'InvalidOffsetError';\n }\n}\n\nexport class InvalidTimeZoneError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `time zone \"${fields.actual ?? ''}\" is not a recognized IANA time zone or fixed offset${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_TIME_ZONE', ...rest },\n );\n this.name = 'InvalidTimeZoneError';\n }\n}\n\n/* c8 ignore start @preserve -- InvalidCalendarError is part of the\n public error-class surface (exported from index.ts, code\n 'INVALID_CALENDAR') but nothing in this package constructs one.\n Checked for a wiring opportunity the same way InvalidTimeZoneError\n got one: there's no user-supplied calendar identifier anywhere in\n the library to validate against a supported list. resolveCalendar()\n in parse.ts derives the calendar entirely from Intl's own resolution\n of the locale string — it's never handed an arbitrary \"calendar\"\n value a caller could get wrong. There's no live input to reject.\n Left unconstructed until the library actually accepts a calendar\n parameter that could be invalid. */\nexport class InvalidCalendarError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `calendar \"${fields.actual ?? ''}\" is not supported${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_CALENDAR', ...rest },\n );\n this.name = 'InvalidCalendarError';\n }\n}\n/* c8 ignore stop @preserve */\n\nexport class AmbiguousInputError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `input \"${fields.input ?? ''}\" is ambiguous${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'AMBIGUOUS_INPUT', ...rest },\n );\n this.name = 'AmbiguousInputError';\n }\n}\n\nexport class InvalidLocaleError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `locale \"${fields.actual ?? ''}\" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_LOCALE', ...rest },\n );\n this.name = 'InvalidLocaleError';\n }\n}\n\nexport class InvalidDurationError extends TemporalFmtError {\n constructor(fields: Omit<TemporalFmtErrorFields, 'code'> & { message?: string }) {\n const { message, ...rest } = fields;\n super(\n message ?? `duration is invalid${fields.reason ? `: ${fields.reason}` : ''}.`,\n { code: 'INVALID_DURATION', ...rest },\n );\n this.name = 'InvalidDurationError';\n }\n}\n\n// Wraps a plain Error thrown from a code path that hasn't been\n// migrated to typed errors yet. Carries the original message in\n// `reason` so callers reading the typed surface still see what\n// failed. Used by safeParse() in parse.ts.\n//\n// As of the 0.9.0 migration, every throw site on the parse/format data\n// path throws a TemporalFmtError directly, so safeParse's `instanceof\n// TemporalFmtError` check always passes before this function would be\n// called — nothing in the current test suite reaches any branch below.\n// Kept as the safety net for wrapping a future unmigrated throw site\n// (see the c8-ignored call in parse.ts's safeParse), same reasoning as\n// that call site: removing this would silently drop the \"safeParse\n// always returns a TemporalFmtError\" contract the moment anyone adds a\n// throw new Error(...) without wiring a typed class for it.\n/* c8 ignore start @preserve -- unreachable from the current test suite,\n see rationale above */\nexport function wrapUntypedError(err: Error, context: { input?: string; format?: string }): TemporalFmtError {\n // Try to classify by inspecting the message — covers the existing\n // parse()/format() throw sites without modifying them. Anything\n // that doesn't match a known pattern falls through to a generic\n // ParseMismatchError, which still has the structured fields.\n const msg = err.message;\n if (/unknown token|isn't a recognized token/.test(msg)) {\n return new UnknownTokenError({ input: context.input, format: context.format, reason: msg });\n }\n if (/ambiguous/i.test(msg)) {\n return new AmbiguousInputError({ input: context.input, format: context.format, reason: msg });\n }\n if (/offset/.test(msg) && /out of range|exceeds|doesn't match the shape/i.test(msg)) {\n return new InvalidOffsetError({ input: context.input, format: context.format, reason: msg });\n }\n if (/no valid pattern matches/i.test(msg)) {\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n }\n if (/doesn't describe a valid date\\/time|incomplete date|weekday token|quarter token/.test(msg)) {\n return new InvalidDateError({ input: context.input, format: context.format, reason: msg });\n }\n const lowerMsg = msg.toLowerCase();\n const mentionsLocale = lowerMsg.includes('locale');\n if (\n (mentionsLocale && (lowerMsg.includes('produced no') || lowerMsg.includes('not a valid'))) ||\n lowerMsg.includes('cutoffs must be')\n ) {\n return new InvalidLocaleError({ input: context.input, format: context.format, reason: msg });\n }\n if (/format string exceeds maximum length|input exceeds maximum length|unterminated quote|isn't a recognized token/i.test(msg)) {\n return new FormatSyntaxError({ input: context.input, format: context.format, reason: msg });\n }\n return new ParseMismatchError({ input: context.input, format: context.format, reason: msg });\n}\n/* c8 ignore stop @preserve */","// Name lists for the locale-aware tokens (MMMM, MMM, EEEE, EEE, a). Each\n// list 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\nimport { InvalidLocaleError } from './errors.js';\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\n// Custom vocabs registered by callers for locales Intl doesn't cover well\n// (e.g. a 13-month Hebrew leap year, where Intl's 12-month vocabulary\n// silently loses a whole month). Keyed by canonical cache key so the\n// same locale string spelling variants fold together — same convention\n// as the Intl-derived vocab cache above.\nconst customVocabs = new Map<string, LocaleVocab>();\nconst MAX_CUSTOM_VOCABS = 500;\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_ENTRY_LENGTH = 256;\n\nfunction assertValidVocab(vocab: Partial<LocaleVocab>, locale: string): void {\n // Strict shape validation at registration time, not lazily on first\n // use — the README's promise is that a malformed registration throws\n // descriptively here, rather than failing later inside format()/parse()\n // with a confusing \"no month part\" or wrong-month error the caller\n // can't trace back to the bad registration.\n const required: Array<{ key: keyof LocaleVocab; length: number; label: string }> = [\n { key: 'monthLong', length: 12, label: 'long month names' },\n { key: 'monthShort', length: 12, label: 'short month names' },\n { key: 'weekdayLong', length: 7, label: 'long weekday names' },\n { key: 'weekdayShort', length: 7, label: 'short weekday names' },\n { key: 'dayPeriod', length: 2, label: 'day period markers (AM/PM-equivalent)' },\n ];\n\n for (const { key, length, label } of required) {\n const value = vocab[key];\n if (value === undefined) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\" is missing required field \"${key}\" (${label}).`\n );\n }\n if (!Array.isArray(value)) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must be an array, got ${typeof value}.`\n );\n }\n if (value.length !== length) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}\" must have exactly ${length} entries (got ${value.length}) — ${label}.`\n );\n }\n value.forEach((entry, i) => {\n if (typeof entry !== 'string' || entry.length === 0) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string, got ${String(entry)}.`\n );\n }\n if (entry.length > MAX_VOCAB_ENTRY_LENGTH) {\n throw new RangeError(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": \"${key}[${i}]\" is too long (maximum ${MAX_VOCAB_ENTRY_LENGTH} characters).`\n );\n }\n });\n }\n\n // Reuse the same collision check the Intl-derived path uses — a\n // duplicate month name is just as ambiguous when supplied by a caller\n // as when produced by Intl.\n assertNoCollision(vocab.monthLong!, 'MMMM month', locale);\n assertNoCollision(vocab.monthShort!, 'MMM month', locale);\n assertNoCollision(vocab.weekdayLong!, 'EEEE weekday', locale);\n assertNoCollision(vocab.weekdayShort!, 'EEE weekday', locale);\n\n // dayPeriod entries must differ from each other, or parse()'s\n // isPM check (raw === vocab.dayPeriod[1]) can never return true and\n // every 12-hour parse silently resolves to AM. The Intl-derived path\n // dedupes a same-AM/PM collision to length 1, but a caller passing\n // both entries identical is a real bug to surface — not something to\n // dedupe around.\n if (vocab.dayPeriod![0] === vocab.dayPeriod![1]) {\n throw new Error(\n `temporal-fmt: registerLocaleVocab for locale \"${locale}\": dayPeriod entries must differ ` +\n `(both are \"${vocab.dayPeriod![0]}\"); otherwise parse() can't tell AM from PM.`\n );\n }\n}\n\n// Anything that caches a result derived from *which* vocabulary is\n// active for a locale (right now: parse.ts's compiled pattern cache,\n// whose MMMM/MMM/EEEE/EEE/a fragments embed the vocab's alternations)\n// subscribes here so it gets invalidated when a registration swaps the\n// vocab — mirrors temporalProvider.ts's subscribeToTemporalChanges.\nconst onVocabChanged: Array<() => void> = [];\n\nexport function subscribeToVocabChanges(listener: () => void): void {\n onVocabChanged.push(listener);\n}\n\n/**\n * Supply a custom month/weekday/day-period vocabulary for a locale key,\n * overriding the Intl-derived vocab this library would otherwise build\n * for that key. Useful for locales Intl doesn't cover well — the README's\n * known-limitations section calls out the Hebrew leap-month gap as a\n * specific case this addresses.\n *\n * Throws descriptively on malformed input (wrong array lengths, empty\n * strings, duplicate entries, missing fields) rather than failing later\n * during format/parse.\n *\n * Registered vocab takes precedence over the Intl-derived vocab for that\n * locale key, including for already-cached entries — registering\n * invalidates the prior cache entry for that locale so the next call\n * picks up the new vocab.\n *\n * @example\n * registerLocaleVocab('en-u-ca-hebrew-leap', {\n * monthLong: ['Nisan','Iyar','Sivan','Tammuz','Av','Elul','Tishrei','Marcheshvan','Kislev','Tevet','Shevat','Adar I','Adar II'],\n * monthShort: ['Nis','Iyy','Siv','Tam','Av','Elu','Tish','Chesh','Kis','Tev','Shv','Ad1','Ad2'],\n * weekdayLong: ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'],\n * weekdayShort: ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'],\n * dayPeriod: ['AM','PM'],\n * });\n */\nexport function registerLocaleVocab(locale: string, vocab: Partial<LocaleVocab>): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new Error(`temporal-fmt: registerLocaleVocab requires a non-empty locale string, got ${String(locale)}.`);\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab locale is too long (maximum ${MAX_LOCALE_TAG_LENGTH} characters).`);\n }\n assertValidVocab(vocab, locale);\n\n const cacheKey = canonicalCacheKey(locale);\n if (!customVocabs.has(cacheKey) && customVocabs.size >= MAX_CUSTOM_VOCABS) {\n throw new RangeError(`temporal-fmt: registerLocaleVocab reached the ${MAX_CUSTOM_VOCABS}-locale limit.`);\n }\n customVocabs.set(cacheKey, {\n monthLong: [...vocab.monthLong!],\n monthShort: [...vocab.monthShort!],\n weekdayLong: [...vocab.weekdayLong!],\n weekdayShort: [...vocab.weekdayShort!],\n dayPeriod: [...vocab.dayPeriod!],\n });\n // Invalidate the Intl-derived cache entry so any prior format/parse\n // result cached for this locale is rebuilt against the new vocab. Not\n // strictly necessary (getLocaleVocab checks customVocabs first), but\n // cheap and keeps the two caches from drifting out of sync.\n vocabCache.delete(cacheKey);\n // Also invalidate downstream caches that baked the previous vocab in\n // at build time (parse.ts's pattern cache is the one that matters:\n // without this, a cached pattern would keep matching the OLD month /\n // weekday names while format() renders the new ones, so the library's\n // own format() output would fail to parse back).\n for (const listener of onVocabChanged) listener();\n}\n\n// Every locale-keyed cache in this library (this one, formatterCache in\n// tokens.ts, patternCache/calendarCache in parse.ts) used to key on the\n// exact locale string a caller passed in. Intl treats spelling variants of\n// the same locale as equivalent ('en-US' / 'en-us' / 'en_US' all resolve\n// the same way), but a plain string-keyed Map doesn't — so callers mixing\n// spellings for what's really one locale would silently fragment across\n// separate cache entries instead of sharing one, making the bounded\n// eviction limits (MAX_*_CACHE_SIZE) less effective than they look. This\n// doesn't change any cache's *correctness* (each entry is still built from\n// -- and valid for -- whatever locale string produced it), only how many\n// distinct entries equivalent spellings end up costing. Falls back to the\n// original string on a malformed/unrecognized tag rather than throwing —\n// cache-key normalization shouldn't be where a bad locale first surfaces\n// as an error; whatever actually calls `new Intl.DateTimeFormat(locale)`\n// downstream is the right place for that.\n// Intl constructors (DateTimeFormat/NumberFormat/RelativeTimeFormat)\n// reject underscore-separated tags like 'en_US' outright, while\n// canonicalCacheKey and every locale-parsing path in this library\n// tolerates them by normalizing to BCP-47 hyphens. Centralize that\n// normalization so every `new Intl.*(locale)` construction site\n// accepts the same spellings the cache keys do. Genuinely malformed\n// tags still throw downstream, unchanged.\nexport function normalizeLocaleTag(locale: string): string {\n return locale.replace(/_/g, '-');\n}\n\n// Memoized so hot paths (parse()'s resolveCalendar keys off this on\n// every call, and every locale-keyed cache re-derives it) don't build a\n// fresh Intl.Locale per invocation — construction is comparatively\n// expensive. Bounded like every other cache in this library.\nconst canonicalKeyCache = new Map<string, string>();\nconst MAX_CANONICAL_KEY_CACHE = 500;\n\nexport function canonicalCacheKey(locale: string): string {\n const hit = canonicalKeyCache.get(locale);\n if (hit !== undefined) return hit;\n let key: string;\n try {\n // Intl.Locale requires BCP-47 hyphens and rejects underscore-separated\n // tags like 'en_US' outright (RangeError), rather than normalizing\n // them — so without this replace, that spelling would just fall\n // through to the catch below and never fold with 'en-US'.\n key = new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n // Malformed tag: key on the raw string. Callers that must reject\n // malformed tags do it via assertValidLocaleTag() before/instead of\n // relying on this function — cache-key normalization isn't where a\n // bad locale should surface as an error (unchanged behavior).\n key = locale;\n }\n if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {\n const oldestKey = canonicalKeyCache.keys().next().value;\n if (oldestKey !== undefined) canonicalKeyCache.delete(oldestKey);\n }\n canonicalKeyCache.set(locale, key);\n return key;\n}\n\n// Single validation choke point for locale tags at public boundaries.\n// Intl constructors reject malformed tags with a bare engine RangeError\n// (\"Incorrect locale information provided\") that carries none of the\n// library's structured error context; this rethrows as the typed\n// InvalidLocaleError with the offending tag in its fields. Accepts the\n// same spellings as canonicalCacheKey (underscore tags normalized).\nexport function assertValidLocaleTag(locale: string): void {\n try {\n new Intl.Locale(normalizeLocaleTag(locale));\n } catch {\n throw new InvalidLocaleError({\n actual: locale,\n reason: 'not a valid BCP-47 locale tag',\n });\n }\n}\n\nconst vocabCache = new Map<string, LocaleVocab>();\nconst MAX_VOCAB_CACHE_SIZE = 500;\n\n// Some locales (ja-JP) split a field across two parts — month \"8\" plus a\n// counter suffix \"月\" as a separate sibling \"literal\" — while format()'s\n// tokens go through toLocaleString(), which concatenates everything into\n// \"8月\". Reading only the type-tagged part used to drop that suffix, so\n// this locale's vocab never matched what format() actually produced.\n// Only merges *adjacent* literals, not the whole string, since the\n// dayPeriod/weekday formatters below carry an extra hour part that a\n// join-everything approach would wrongly absorb.\nfunction partValue(formatter: Intl.DateTimeFormat, date: Date, type: Intl.DateTimeFormatPartTypes): string {\n const parts = formatter.formatToParts(date);\n const index = parts.findIndex((p) => p.type === type);\n /* c8 ignore start @preserve -- defensive guard against a real but\n unreproducible failure mode: an Intl implementation that omits the\n requested part type entirely for some locale. Checked every locale\n with unusual dayPeriod/weekday/month rendering available in this\n runtime's ICU data (ja-JP, zh-CN, th-TH, he-IL, ar-SA, ko-KR, fa-IR)\n against the exact formatter options getLocaleVocab uses (notably\n hour12: true for the dayPeriod formatter, which is what makes every\n locale here actually emit a dayPeriod part — omitting it is what\n produced a false \"gap\" during investigation). None omit their part\n on this runtime. A different ICU version or a non-Node Intl\n implementation could plausibly behave differently, so this stays a\n real check rather than an assertion. */\n /* c8 ignore start @preserve */\n if (index === -1) {\n throw new InvalidLocaleError({\n message: `temporal-fmt: locale produced no \"${type}\" part while building match vocabulary.`,\n });\n }\n /* c8 ignore stop @preserve */\n let value = parts[index]!.value;\n const prev = parts[index - 1];\n const next = parts[index + 1];\n // skip whitespace literals (the separator before \"AM\") — only a\n // no-space suffix like ja-JP's \"月\" should get folded in\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\n// Two entries rendering identically means parse()'s reverse lookup\n// (indexOf) can never tell them apart. Weekday collisions already surface\n// via parse()'s dayOfWeek cross-check, but with a confusing same-string\n// error; months have no equivalent cross-check, so a collision there would\n// otherwise resolve silently to the wrong month. Catching both here, once\n// at build time, gives one clear error instead.\nfunction assertNoCollision(names: string[], label: string, locale: string): void {\n const seen = new Map<string, number>();\n for (let i = 0; i < names.length; i++) {\n const prior = seen.get(names[i]!);\n if (prior !== undefined) {\n // Not migrated to a typed error: this function is shared between\n // getLocaleVocab's Intl-derived path (data-path error, would be a\n // good InvalidLocaleError candidate) and assertValidVocab's\n // registration-time check (out of scope for this pass — see the\n // localeVocab.ts registration-error follow-up). Splitting this into\n // two near-duplicate functions just to route error types\n // differently isn't worth it for one throw; revisit together with\n // the registration-error work instead.\n throw new Error(\n `temporal-fmt: locale \"${locale}\" renders ${label} index ${prior} and ${i} identically ` +\n `(\"${names[i]}\"). parse() can't reliably tell these apart for this locale/token, so this ` +\n `combination isn't supported.`\n );\n }\n seen.set(names[i]!, i);\n }\n}\n\n// Exposed for tokens.ts: when a custom vocab is registered for this\n// locale, format()'s locale-aware tokens (MMMM/MMM/EEEE/EEE/a) read\n// straight from the registered array instead of going through\n// Intl.DateTimeFormat. Without this override, format() would silently\n// keep producing Intl's strings while parse() matched against the\n// registered vocab — the two would round-trip-fail.\nexport function getCustomVocab(locale: string): LocaleVocab | undefined {\n const cacheKey = canonicalCacheKey(locale);\n return customVocabs.get(cacheKey);\n}\n\nexport function getLocaleVocab(locale: string): LocaleVocab {\n // Validate before building vocab: a malformed tag used to surface as a\n // raw RangeError from the first new Intl.DateTimeFormat below.\n assertValidLocaleTag(locale);\n const cacheKey = canonicalCacheKey(locale);\n // Registered vocabs take precedence over the Intl-derived one — this\n // is the override mechanism registerLocaleVocab() promises. Checking\n // here, before the Intl cache lookup, means a registration that\n // happens *after* the first Intl-derived vocab was built still takes\n // effect on the next call.\n const custom = customVocabs.get(cacheKey);\n if (custom) {\n return custom;\n }\n const cached = vocabCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const intlLocale = normalizeLocaleTag(locale);\n const monthLongFmt = new Intl.DateTimeFormat(intlLocale, { month: 'long', timeZone: 'UTC' });\n const monthShortFmt = new Intl.DateTimeFormat(intlLocale, { 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 assertNoCollision(monthLong, 'MMMM month', locale);\n assertNoCollision(monthShort, 'MMM month', locale);\n\n const weekdayLongFmt = new Intl.DateTimeFormat(intlLocale, { weekday: 'long', timeZone: 'UTC' });\n const weekdayShortFmt = new Intl.DateTimeFormat(intlLocale, { 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 // redundant with parse()'s dayOfWeek cross-check, but gives a clearer error\n assertNoCollision(weekdayLong, 'EEEE weekday', locale);\n assertNoCollision(weekdayShort, 'EEE weekday', locale);\n\n const dayPeriodFmt = new Intl.DateTimeFormat(intlLocale, { 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 if (vocabCache.size >= MAX_VOCAB_CACHE_SIZE) {\n const oldestKey = vocabCache.keys().next().value;\n if (oldestKey !== undefined) vocabCache.delete(oldestKey);\n }\n vocabCache.set(cacheKey, vocab);\n return vocab;\n}","// ISO 8601 week numbering: a week runs Monday–Sunday, and week 1 of a year\n// is the week containing the year's first Thursday (equivalently, the week\n// containing January 4). This means late-December dates can fall in week 1\n// of the *next* year, and early-January dates can fall in week 52 or 53 of\n// the *previous* year. The \"ISO week-numbering year\" (what `RRRR` formats)\n// is that adjacent year, not the calendar year.\n//\n// Computed here from the date's own year/month/day plus its ISO dayOfWeek\n// (1=Mon..7=Sun, matching Temporal's numbering) using plain Gregorian\n// arithmetic — no Temporal factory needed. format() only has the fields\n// the caller already put on the object, and requiring a Temporal\n// implementation just for ISO week would be a regression for callers who\n// use format() without setTemporal() on a non-26 Node.\n\nconst DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];\n\nexport function isGregorianLeapYear(year: number): boolean {\n // Gregorian rule: divisible by 4, except centuries which must also be\n // divisible by 400. Temporal's iso8601 calendar is proleptic Gregorian\n // (no Julian cutover), so this applies for every year, including BCE.\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nfunction daysInYear(year: number): 365 | 366 {\n return isGregorianLeapYear(year) ? 366 : 365;\n}\n\nexport function dayOfYear(year: number, month: number, day: number): number {\n let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1]! + day;\n if (month > 2 && isGregorianLeapYear(year)) doy += 1;\n return doy;\n}\n\n// Jan 1, 2000 was a Saturday — ISO dayOfWeek 6. Anchoring day-of-week\n// computations to a known reference date is simpler and cheaper than\n// pulling in Zeller's congruence, and the reference never changes.\nconst REFERENCE_YEAR = 2000;\nconst REFERENCE_JAN1_DAY_OF_WEEK = 6;\n\nfunction dayOfWeekOfJan1(year: number): number {\n // Sum full-year deltas from the 2000 anchor rather than recomputing from\n // scratch each call — the per-call work is a single mod this way, and\n // the loop rarely runs far (a typical caller passes a current-era year).\n let offset = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);\n }\n // Convert \"days since Jan 1, 2000\" into an ISO day-of-week (1=Mon..7=Sun).\n // Jan 1, 2000 was ISO 6 (Sat), so zero-indexed dow = (6-1 + offset) mod 7.\n const zeroIndexed = (((6 - 1 + offset) % 7) + 7) % 7;\n return zeroIndexed + 1;\n}\n\nexport interface IsoWeekDate {\n isoYear: number;\n week: number; // 1..53\n}\n\nexport function isoWeekYearAndWeek(year: number, month: number, day: number, dayOfWeek: number): IsoWeekDate {\n // Step 1: find the Thursday of the current ISO week. The ISO week-numbering\n // year is whichever calendar year that Thursday falls in. Computing it via\n // day-of-year offsets (rather than constructing a Temporal.PlainDate and\n // adding days) keeps this function pure-numeric.\n const doy = dayOfYear(year, month, day);\n const thursdayDoyRelative = doy + (4 - dayOfWeek); // may be <1 or >daysInYear\n\n let isoYear: number;\n let thursdayDoy: number;\n if (thursdayDoyRelative < 1) {\n isoYear = year - 1;\n thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);\n } else if (thursdayDoyRelative > daysInYear(year)) {\n isoYear = year + 1;\n thursdayDoy = thursdayDoyRelative - daysInYear(year);\n } else {\n isoYear = year;\n thursdayDoy = thursdayDoyRelative;\n }\n\n // Step 2: locate the first Thursday of isoYear — its week is week 1. The\n // first Thursday's day-of-year depends on what weekday Jan 1 of isoYear is.\n const jan1Dow = dayOfWeekOfJan1(isoYear);\n const firstThursdayDoy = 1 + ((4 - jan1Dow + 7) % 7); // 1..7\n\n // Step 3: count full weeks between the two Thursdays.\n const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);\n return { isoYear, week };\n}\n","import { getTemporal, subscribeToTemporalChanges } from './temporalProvider.js';\nimport { canonicalCacheKey, getCustomVocab, normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\nimport { isoWeekYearAndWeek, dayOfYear } from './isoWeek.js';\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\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 // 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 const { week } = isoWeekYearAndWeek(t.year!, t.month!, t.day!, t.dayOfWeek!);\n return pad(week, 2);\n }, 'dayOfWeek'],\n ['RRRR', (t) => {\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) => String(dayOfYear(t.year!, t.month!, t.day!)), 'day'],\n ['DD', (t) => pad(dayOfYear(t.year!, t.month!, t.day!), 2), 'day'],\n ['DDD', (t) => pad(dayOfYear(t.year!, t.month!, t.day!), 3), '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];","import { TOKENS } from './tokens.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nexport type Piece =\n | { kind: 'token'; value: string }\n | { kind: 'literal'; value: string };\n\n// longest-first so the greedy scan never matches \"M\" when \"MMMM\" was there\nconst SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);\n\n/**\n * Splits a format string like `\"yyyy-MM-dd 'at' HH:mm\"` into token/literal\n * pieces. Text in single quotes is always literal (e.g. write 'rd' in\n * \"3rd\" so it's not read as the day token). A doubled quote ('') means a\n * literal quote character, both inside a quoted span and standalone.\n */\nexport function tokenize(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n\n while (i < format.length) {\n const ch = format[i];\n\n if (ch === \"'\") {\n // check doubled-quote first or \"''best''\" parses wrong\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < format.length) {\n if (format[j] === \"'\") {\n if (format[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += format[j];\n j += 1;\n }\n\n if (!closed) {\n throw new FormatSyntaxError({\n format,\n message: `temporal-fmt: unterminated quote in format string \"${format}\"`,\n });\n }\n\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n\n const match = SORTED_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // match is already the longest registered token starting at i, so\n // one more of its last character can't be a token of its own —\n // it'd fall through to whatever token or literal rule handles that\n // character next, silently splicing an unrelated field onto this\n // one (e.g. \"zzzz\" used to read as zzz + literal \"z\", \"MMMMM\" as\n // MMMM + the numeric-month token M). Treat the whole overlong run\n // as one unrecognized token instead.\n const runChar = match[match.length - 1];\n if (format[i + match.length] === runChar) {\n let end = i + match.length;\n while (format[end] === runChar) end += 1;\n // UnknownTokenError, not FormatSyntaxError: wrapUntypedError's\n // classifier already treats this exact message (\"isn't a\n // recognized token\") as UNKNOWN_TOKEN (see errors.test.js's\n // \"overlong token run classifies as UnknownTokenError\"), and a\n // direct throw here needs to agree with what safeParse's fallback\n // path would have classified it as.\n throw new UnknownTokenError({\n format,\n token: format.slice(i, end),\n message:\n `temporal-fmt: \"${format.slice(i, end)}\" in format string \"${format}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n\n // not a token or quote — pass through as-is\n appendLiteral(pieces, ch);\n i += 1;\n }\n\n return pieces;\n}\n\n// merges into the previous piece if it's also a literal, so \"---\" is one\n// piece instead of three\nfunction appendLiteral(pieces: Piece[], value: string): void {\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += value;\n } else {\n pieces.push({ kind: 'literal', value });\n }\n}","// Numbering systems. Default: latn (ASCII digits).\n// Optional: arab, deva, beng, etc. Configurable on formatting.\n//\n// The library's existing tokens always render ASCII digits (see\n// tokens.ts's pad() — uses String(n) which produces ASCII). This\n// module adds the ability to convert the output to a locale's native\n// digits via Intl.NumberFormat, which is the standard mechanism JS\n// provides for digit transliteration.\n//\n// On the parse side: parse() only accepts ASCII digits, matching how\n// the existing NUMERIC_FRAGMENTS regex is built. A parseNumberingSystem\n// option could convert input digits to ASCII before matching — but\n// that's a per-call opt-in, not silent acceptance of every numeral\n// system.\n\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport type NumberingSystem = 'latn' | 'arab' | 'deva' | 'beng' | 'guru' | 'gujr' | 'orya' | 'tamldec' | 'telu' | 'knda' | 'mlym' | 'fullwide' | 'hanidec';\n\n// All supported NumberingSystem values. latn is the default and what\n// the rest of the library produces natively.\nexport const SUPPORTED_NUMBERING_SYSTEMS: ReadonlySet<string> = new Set([\n 'latn', 'arab', 'deva', 'beng', 'guru', 'gujr', 'orya', 'tamldec',\n 'telu', 'knda', 'mlym', 'fullwide', 'hanidec',\n]);\n\nconst digitMapCache = new Map<string, Record<string, string>>();\n\n// Builds a per-numbering-system digit transliteration map. Uses\n// Intl.NumberFormat to render 0-9 in the requested system, then\n// builds the lookup table. Cached because constructing a formatter\n// is expensive and we re-use the same map for every digit in the\n// output.\nfunction getDigitMap(system: string): Record<string, string> {\n let map = digitMapCache.get(system);\n if (map) return map;\n /* c8 ignore start @preserve -- dead by construction, not just\n untested: both callers (convertDigits, convertDigitsToAscii) already\n return early on system === 'latn' before calling getDigitMap at all,\n so this function is never invoked with 'latn'. Kept as a defensive\n fallback rather than trusting that stays true for any future\n caller. */\n if (system === 'latn') {\n map = {};\n for (let i = 0; i < 10; i++) map[String(i)] = String(i);\n } else {\n /* c8 ignore stop @preserve */\n const fmt = new Intl.NumberFormat('en-US-u-nu-' + system, { useGrouping: false });\n map = {};\n for (let i = 0; i < 10; i++) {\n map[String(i)] = fmt.format(i);\n }\n }\n digitMapCache.set(system, map);\n return map;\n}\n\n// Converts every ASCII digit in `s` to its equivalent in the requested\n// numbering system. Non-digit characters pass through unchanged.\nexport function convertDigits(s: string, system: string): string {\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(', ')}.` });\n }\n const map = getDigitMap(system);\n let result = '';\n for (const ch of s) {\n if (ch >= '0' && ch <= '9') {\n // map[ch] is always populated for '0'-'9' since getDigitMap builds\n // all ten digit keys for every supported system and ch is already\n // range-checked above; the ?? ch fallback exists only as a\n // type-safety guard against Record's implicit undefined, not a\n // reachable runtime path.\n /* c8 ignore next */\n result += map[ch] ?? ch;\n } else {\n result += ch;\n }\n }\n return result;\n}\n\n// Parses a string with non-ASCII digits back to ASCII. The inverse of\n// convertDigits — used by parse() when an explicit `numberingSystem`\n// option is passed. Throws on unsupported systems.\nexport function convertDigitsToAscii(s: string, system: string): string {\n // Same dead-by-construction situation as applyParseNumbering's own\n // 'latn' guard: this function's only caller (applyParseNumbering)\n // already returns early on system === 'latn' before reaching this\n // call, so system is never 'latn' here in practice. Kept as a\n // defensive guard for any future direct caller.\n /* c8 ignore next */\n if (system === 'latn') return s;\n if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {\n throw new InvalidLocaleError({ actual: system, reason: `numbering system \"${system}\" is not supported.` });\n }\n const map = getDigitMap(system);\n // Build reverse map.\n const reverse: Record<string, string> = {};\n for (const k of Object.keys(map)) reverse[map[k]!] = k;\n let result = '';\n for (const ch of s) {\n result += reverse[ch] ?? ch;\n }\n return result;\n}\n\n// Augmented FormatOptions that includes the numberingSystem field.\n// Callers pass { numberingSystem: 'arab' } to format() to get Arabic-\n// Indic digits in the output.\nexport interface NumberingFormatOptions extends FormatOptions {\n numberingSystem?: string;\n}\n\n// Parse-side counterpart. Named parseNumberingSystem (not numberingSystem)\n// so a caller mixing format() and parse() options in the same config\n// object can set both independently — the two directions aren't always\n// symmetric (e.g. converting *to* native digits on output without\n// expecting native digits back on input, or vice versa).\nexport interface NumberingParseOptions extends FormatOptions {\n parseNumberingSystem?: string;\n}\n\n// Helper for the format path: takes the formatted ASCII output of\n// format() and converts digits if a numberingSystem was requested.\n// Kept here so format.ts doesn't need to know about numbering systems.\nexport function applyNumbering(s: string, options: NumberingFormatOptions): string {\n const system = options.numberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigits(s, system);\n}\n\n// Helper for the parse path: converts input digits to ASCII before\n// matching, when parseNumberingSystem is set. Distinct from the format\n// path's option name (numberingSystem vs parseNumberingSystem) so a\n// caller can be explicit about which direction they want transliterated.\nexport function applyParseNumbering(s: string, options: { parseNumberingSystem?: string }): string {\n // Both call sites of applyParseNumbering (parse.ts) already guard with\n // `if (options.parseNumberingSystem)` before calling it, so\n // options.parseNumberingSystem is always truthy here — the ?? 'latn'\n // fallback, and the subsequent 'latn' early return, are dead by\n // construction, not just untested. Kept as a defensive default rather\n // than assuming every future caller replicates the guard.\n /* c8 ignore next 2 */\n const system = options.parseNumberingSystem ?? 'latn';\n if (system === 'latn') return s;\n return convertDigitsToAscii(s, system);\n}\n\n// Suppress unused-import warning. DEFAULT_LOCALE is imported for the\n// type augmentation pattern above; if the project ever exports a\n// typed-locale interface, it'll be the source of truth for the default.\nvoid DEFAULT_LOCALE;","import { TOKENS, DEFAULT_LOCALE, type TemporalLike, type FormatOptions } from './tokens.js';\nimport { tokenize, type Piece } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { applyNumbering, type NumberingFormatOptions } from './numbering.js';\nimport { FormatSyntaxError, UnknownTokenError } from './errors.js';\n\nconst HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));\n\n// Pre-tokenized format strings, keyed by (formatStr) — locale doesn't\n// change the tokenization step, only the per-token rendering, so the\n// piece list is shared across locales. Same eviction shape as the\n// other caches in this library.\nconst tokenizeCache = new Map<string, Piece[]>();\nconst MAX_TOKENIZE_CACHE_SIZE = 500;\n\nfunction getPieces(formatStr: string): Piece[] {\n let pieces = tokenizeCache.get(formatStr);\n if (pieces) return pieces;\n if (tokenizeCache.size >= MAX_TOKENIZE_CACHE_SIZE) {\n const oldestKey = tokenizeCache.keys().next().value;\n if (oldestKey !== undefined) tokenizeCache.delete(oldestKey);\n }\n pieces = tokenize(formatStr);\n tokenizeCache.set(formatStr, pieces);\n return pieces;\n}\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 *\n * Throws on a token the input type doesn't support (e.g. 'HH' on a PlainDate).\n */\nexport function format(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): string {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\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 = getPieces(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 /* c8 ignore start @preserve -- unreachable: tokenize() only ever emits\n tokens present in TOKENS, and HANDLER_BY_TOKEN is built from that\n same list, so every token piece.value can hold already has an entry\n here. Same defensive-guard category as the twin checks in\n analyze.ts/formatDuration.ts. */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 // Numeral transliteration happens last and only on request — every\n // upstream token handler still emits plain ASCII digits, so this is\n // the single place output digits can diverge from that default.\n return applyNumbering(result, options);\n}\n\n// Shape mirrors Intl.DateTimeFormat.formatToParts: each entry is either\n// a literal (carrying no token info) or a token piece (carrying the\n// token string and the formatted value). Letting callers iterate parts\n// means they can build custom output — strip a token, swap a separator,\n// render each token to its own DOM node — without re-implementing the\n// tokenizer or the field-check logic.\nexport interface FormattedPart {\n type: 'literal' | 'token';\n value: string;\n // Present when `type === 'token'`. Carries the token string (e.g. \"yyyy\")\n // so a caller can look up its metadata via tokenInfo() from analyze.ts.\n token?: string;\n}\n\nexport function formatToParts(temporal: TemporalLike, formatStr: string, options: NumberingFormatOptions = {}): FormattedPart[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pieces = getPieces(formatStr);\n const result: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n // Collapse adjacent literals so formatToParts stays consistent with\n // how format() walks pieces — a multi-char literal \"at \" is one\n // entry, not one per character. Same merge logic as appendLiteral\n // in tokenize.ts.\n const last = result[result.length - 1];\n /* c8 ignore start @preserve -- unreachable through tokenize()'s own\n output: appendLiteral() in tokenize.ts already merges every\n adjacent literal character before pieces ever reaches here, so\n two 'literal' pieces never sit back-to-back in the array this\n loop walks. Kept as defense-in-depth in case that invariant ever\n changes upstream. */\n if (last && last.type === 'literal') {\n last.value += piece.value;\n } else {\n /* c8 ignore stop @preserve */\n result.push({ type: 'literal', value: piece.value });\n }\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the note in format() above */\n if (!handler) {\n throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n }\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 // Numeral transliteration applies per-token-part here, rather than\n // once at the end like format() does, so a caller styling individual\n // parts (e.g. one <span> per token) still gets correctly-transliterated\n // digits in each part instead of plain ASCII.\n result.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return result;\n}\n\n// Pre-compiles a format string into an object whose format()/formatToParts()\n// methods skip the tokenization step on every call. The tokenizeCache in\n// this module means a plain format(temporal, fmt) call already pays only\n// a Map lookup for tokenization after the first call, so compileFormat()\n// is mostly a typing/ergonomics affordance — useful for callers who want\n// to hold onto a compiled form explicitly (e.g. to inspect the pieces\n// via the .pieces property, or to pass the compiled object around\n// instead of the string).\nexport interface CompiledFormat {\n format(temporal: TemporalLike, options?: NumberingFormatOptions): string;\n formatToParts(temporal: TemporalLike, options?: NumberingFormatOptions): FormattedPart[];\n readonly pieces: ReadonlyArray<Piece>;\n readonly formatStr: string;\n}\n\nexport function compileFormat(formatStr: string): CompiledFormat {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n // Pre-tokenize once. Validation (unknown tokens, unterminated quotes)\n // happens here, not lazily on first format() call — surfaces a bad\n // format string at compile time rather than at first use, which is\n // the point of compiling up front.\n const pieces = getPieces(formatStr);\n return {\n formatStr,\n pieces,\n format(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 result += handler.fn(temporal, locale);\n }\n return applyNumbering(result, options);\n },\n formatToParts(temporal: TemporalLike, options: NumberingFormatOptions = {}) {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore next -- unreachable, see the note in formatToParts() above */\n if (last && last.type === 'literal') last.value += piece.value;\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = HANDLER_BY_TOKEN.get(piece.value);\n /* c8 ignore next -- unreachable, see the note in format() above */\n if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token \"${piece.value}\"` });\n if (temporal[handler.field] === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n token: piece.value,\n message:\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 out.push({ type: 'token', value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });\n }\n return out;\n },\n };\n}\n\n// Exported so analyze.ts can reuse the same tokenization cache rather\n// than re-tokenizing when a caller asks for both a format() and an\n// analyzeFormat() on the same string.\nexport function _getPieces(formatStr: string): Piece[] {\n return getPieces(formatStr);\n}\n\n// Exported for analyze.ts — same reason as above. The handler map is\n// the source of truth for \"what field does this token need\" — analyze\n// consumes it to compute requiredFields and compatibleTypes.\nexport function _handlerFor(token: string): { fn: (t: TemporalLike, locale: string) => string; field: keyof TemporalLike } | undefined {\n return HANDLER_BY_TOKEN.get(token);\n}","import { getLocaleVocab } from './localeVocab.js';\nimport { UnknownTokenError } from './errors.js';\nimport { getTemporal } from './temporalProvider.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction alternation(values: string[], caseInsensitive = false): string {\n const escaped = values.map(escapeRegExp);\n if (!caseInsensitive) return `(?:${escaped.join('|')})`;\n // JS regex has no per-group inline case-insensitive flag, and this\n // fragment gets embedded in one larger pattern built with a single flag\n // set — so case-folding here means listing both cases explicitly rather\n // than relying on a flag.\n return `(?:${escaped.map(foldCase).join('|')})`;\n}\n\n// Expands \"PM\" into a character-class-per-letter pattern matching any\n// casing of it (\"[Pp][Mm]\"), so \"pm\", \"Pm\", \"PM\" all match the same\n// alternative. Only used for the day-period token (see the 'a' case\n// below) — not applied to month/weekday names, where case-folding across\n// scripts is a different and riskier problem this doesn't need to solve.\nfunction foldCase(value: string): string {\n return value.replace(/[a-zA-Z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);\n}\n\n// Every real IANA zone id is letters/digits/'_'/'+'/'-' segments joined by\n// '/' (e.g. \"America/Argentina/Buenos_Aires\", \"Etc/GMT+12\"); UTC and\n// fixed-offset strings are the only other shapes zzz accepts. Matching that\n// *shape* here — instead of alternating all ~400 zone names inline — keeps\n// the compiled regex small regardless of how many zzz tokens appear in a\n// format string. The captured text still gets checked against the real\n// zone set in isValidTimeZone() after the overall pattern matches, so this\n// is strictly a matching-cost change, not a validation-strictness change:\n// a bogus zone id fails \"no valid pattern matches\" exactly like it did when\n// the zone list was inlined (see isValidTimeZone's caller in parse.ts).\nconst TIME_ZONE_SHAPE = '(?:UTC|[+-]\\\\d{2}:\\\\d{2}(?::\\\\d{2}(?:\\\\.\\\\d{1,9})?)?|[A-Za-z_]+(?:[+-]\\\\d{1,2})?(?:\\\\/[A-Za-z0-9_+-]+)*)';\n\n// Per-variant regex shapes for the six offset tokens. Each matches the\n// shape its own format counterpart produces, so a round-trip\n// format->parse succeeds for any input the library itself emitted.\n//\n// Kept loose (no hour/minute range bounds inline) on purpose, mirroring\n// how TIME_ZONE_SHAPE is loose: a permissive shape here lets parse()\n// surface a descriptive out-of-range error post-match (see\n// parseOffsetString in parse.ts) instead of the generic \"no valid pattern\n// matches\" the regex throws when the shape itself fails. \"+99:99\" should\n// tell the user it's out of range, not look like the input never matched\n// the format at all.\n//\n// X / x accept an optional minutes group so whole-hour offsets can be\n// written short (\"+05\") while non-whole-hour offsets still parse\n// (\"+0530\"). The optional group is greedy, so for input \"+0530\" the\n// engine prefers the 4-digit match; only falls back to 2-digit when\n// there's nothing else to consume — same longer-first preference the\n// unpadded numeric tokens use elsewhere in this file (see the comment on\n// NUMERIC_FRAGMENTS).\nconst OFFSET_SHAPES: Record<string, string> = {\n X: '(?:Z|[+-]\\\\d{2}(?:\\\\d{2})?)',\n XX: '(?:Z|[+-]\\\\d{4})',\n XXX: '(?:Z|[+-]\\\\d{2}:\\\\d{2})',\n x: '[+-]\\\\d{2}(?:\\\\d{2})?',\n xx: '[+-]\\\\d{4}',\n xxx: '[+-]\\\\d{2}:\\\\d{2}',\n};\n\nfunction getTimeZoneFragment(): string {\n return TIME_ZONE_SHAPE;\n}\n\nlet validZoneSet: Set<string> | undefined;\n\n// Real zone ids plus the couple of aliases zzz has always accepted even\n// though Intl.supportedValuesOf('timeZone') doesn't list them (UTC isn't\n// itself an IANA zone name, it's the identity offset).\nfunction getValidZoneSet(): Set<string> {\n if (!validZoneSet) {\n validZoneSet = new Set(Intl.supportedValuesOf('timeZone'));\n validZoneSet.add('UTC');\n }\n return validZoneSet;\n}\n\nconst FIXED_OFFSET_RE = /^[+-]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,9})?)?$/;\n\n// Called post-match on whatever the bounded TIME_ZONE_SHAPE captured, since\n// that shape is deliberately looser than \"a real zone id\" (it has to be, to\n// stay a fixed-size regex fragment — see the comment above). A fixed offset\n// is valid by construction; anything else has to be a real IANA name.\n//\n// Intl.supportedValuesOf('timeZone') is checked first as a fast path — it's\n// a plain Set lookup and covers the overwhelming majority of real input.\n// But it lists only canonical zone ids, not every IANA link/alias name:\n// \"Asia/Kolkata\" is a legitimate, commonly-used alias for \"Asia/Calcutta\"\n// that Temporal.ZonedDateTime.from() itself resolves correctly, yet some\n// ICU builds' supportedValuesOf() omits it. Rejecting it here — even\n// though the exact same string would construct a real ZonedDateTime one\n// call later — was a real bug: parse() refused input its own downstream\n// construction step would have accepted. Anything Intl doesn't recognize\n// gets a second check against Temporal itself before being refused.\nexport function isValidTimeZone(raw: string): boolean {\n if (FIXED_OFFSET_RE.test(raw) || getValidZoneSet().has(raw)) return true;\n try {\n getTemporal().ZonedDateTime.from({\n year: 2026, month: 1, day: 1, hour: 0, minute: 0, second: 0,\n timeZone: raw,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n// mirrors the ranges pad() in tokens.ts actually produces — keep in sync\n// if those ever change\n//\n// Unpadded alternatives (M, H, h, m, s) list the longer branch first\n// (e.g. '1[0-2]|[1-9]', not '[1-9]|1[0-2]'). This matters only when two\n// unpadded tokens are glued with no separator: with short-first ordering,\n// a regex engine takes the first successful overall match, and won't\n// backtrack into a token's second alternative unless its first choice\n// makes the *rest* of the pattern fail outright. If the short reading also\n// happens to leave a valid match for the next token, the engine stops\n// there — silently, deterministically, and with no relation to which\n// reading a human intended. E.g. \"Md\" against \"121\": short-first order\n// resolves it as month=1/day=21 (M grabs '1', d gets '21', which is a\n// valid day) instead of month=12/day=1. Longer-first ordering fixes this\n// by making the greedy match try to consume as many digits as possible\n// before ever handing digits to the next token, which is the reading\n// that matches how format() itself produces glued output in the first\n// place (format() always emits the token's natural width, so decoding\n// should prefer the same). Found via the token×token combinatorial glue\n// matrix in combinatorial.test.js — see that file for the full case list.\nconst NUMERIC_FRAGMENTS: Record<string, string> = {\n yy: '\\\\d{2}',\n MM: '(?:0[1-9]|1[0-2])',\n M: '(?:1[0-2]|[1-9])',\n dd: '(?:0[1-9]|[12]\\\\d|3[01])',\n d: '(?:[12]\\\\d|3[01]|[1-9])',\n HH: '(?:[01]\\\\d|2[0-3])',\n H: '(?:1\\\\d|2[0-3]|[0-9])',\n hh: '(?:0[1-9]|1[0-2])',\n h: '(?:1[0-2]|[1-9])',\n mm: '(?:[0-5]\\\\d)',\n m: '(?:[1-5]\\\\d|[0-9])',\n ss: '(?:[0-5]\\\\d)',\n s: '(?:[1-5]\\\\d|[0-9])',\n SSSSSSSSS: '\\\\d{9}',\n SSSSSSSS: '\\\\d{8}',\n SSSSSSS: '\\\\d{7}',\n SSSSSS: '\\\\d{6}',\n SSSSS: '\\\\d{5}',\n SSSS: '\\\\d{4}',\n SSS: '\\\\d{3}',\n SS: '\\\\d{2}',\n S: '\\\\d',\n // Q is always a single digit 1-4 (no padding variant, so no width ambiguity\n // with adjacent digit tokens the way M/d/H/m/s have).\n Q: '[1-4]',\n};\n\n// QQQ matches its own formatted output: the literal \"Q\" prefix plus a single\n// digit 1-4. Kept as a static fragment rather than routed through vocab,\n// since the \"Q\" prefix is part of the token's own contract, not locale-vocab\n// data that could ever differ.\nconst QQQ_FRAGMENT = 'Q[1-4]';\n\n// Format-only tokens — they have no parse counterpart. The tokenizer still\n// recognizes them (so format() can use them), but parse()'s regex builder\n// rejects them with a clear error rather than silently dropping the token\n// or falling through to the generic \"unknown token\" message.\n// Exported so analyze.ts can include them in the public analyzer surface.\nexport const FORMAT_ONLY_TOKENS = new Set(['do', 'ww', 'RRRR', 'D', 'DD', 'DDD', 'LLLL', 'LLL', 'cccc', 'ccc', 'GGGG', 'G', 'zzzz', 'z']);\n\n// pad()'s year formatter (tokens.ts) never truncates: it preserves the sign\n// for BCE years and doesn't cap width past 9999, so a formatted \"yyyy\" can\n// be longer than 4 digits or start with '-'. YYYY_EXTENDED accepts that;\n// YYYY_EXACT is the plain 4-unsigned-digit case. Which one a given \"yyyy\"\n// occurrence gets depends on what follows it — see buildCapturingPattern in\n// parsePattern.ts. Two separate fragments instead of one `-?\\d{4,}` because\n// an open-ended-width year directly followed by another digit token (e.g.\n// \"yyyyMM\") lets the year's own greediness silently eat digits meant for\n// the next token — same class of bug as UNPADDED_NUMERIC_TOKENS below, but\n// unbounded-width, so it can't reuse enumerateValidSplits' fixed-range\n// splitting. Restricting to exactly 4 digits whenever something could\n// follow closes that off entirely, at the cost of \"yyyyMM\" not being able\n// to represent a 5-digit year — an already-rare case doubly rare in\n// combination with a glued adjacent token.\nconst YYYY_EXACT = '-?\\\\d{4}';\nconst YYYY_EXTENDED = '-?\\\\d{4,}';\n\n// \"y\" is yyyy's unpadded sibling: any width, 1 digit up through Temporal's\n// max supported year (275760), with the same optional leading \"-\" for\n// years before ISO year 0. Unlike yyyy, there's no bounded fallback to\n// reach for when something digit-consuming follows — yyyy can fall back\n// to an exact 4 digits because it's always exactly 4 digits in that case,\n// but \"y\" being unpadded is the entire point of the token, so there's no\n// narrower shape that still means the same thing. buildCapturingPattern\n// (parsePattern.ts) refuses to build a pattern where \"y\" is immediately\n// followed by another digit-consuming element, rather than trying to\n// bound this fragment the way YYYY_EXACT does.\nconst Y_FRAGMENT = '-?\\\\d+';\n\n// True for any token whose matched text can start with a digit — i.e.\n// every token here except the locale-named ones (MMMM/MMM/EEEE/EEE/a) and\n// zzz (which can start with a digit only via a fixed offset like \"+09:00\",\n// already handled by requiring a leading sign there). Used to decide\n// whether a \"yyyy\" immediately before this token needs the exact-4-digit\n// fragment instead of the open-ended one.\n// Exported for parsePattern.ts's ReDoS guard: a token whose regex\n// fragment can begin with a bare digit (see the guard comments there).\nexport const DIGIT_LEADING_TOKENS = new Set([\n 'yyyy', 'yy', 'y', 'MM', 'M', 'dd', 'd', 'HH', 'H', 'hh', 'h', 'mm', 'm', 'ss', 's',\n 'SSSSSSSSS', 'SSSSSSSS', 'SSSSSSS', 'SSSSSS', 'SSSSS', 'SSSS', 'SSS', 'SS', 'S',\n]);\n\n// Tokens whose regex fragment has no upper bound on width — nothing\n// caps how many digits they might consume. yyyy's YYYY_EXTENDED form is\n// also unbounded, but it's only ever selected when the *next* piece\n// already isn't digit-consuming (see tokenFragment below), so by\n// construction it never reaches a position where the ambiguity guard in\n// parsePattern.ts would need to weigh in. \"y\" has no such self-limiting\n// selection rule — it's unbounded unconditionally — so it needs an\n// explicit guard there instead of a bit-cost estimate: there's no\n// meaningful number of \"width choices\" to assign to \"any number of\n// digits,\" so parsePattern.ts refuses outright rather than pretending a\n// finite ambiguity score covers it.\nexport const UNBOUNDED_WIDTH_TOKENS = new Set(['y']);\n\nexport function tokenFragment(token: string, locale: string, nextToken?: string): string {\n if (token === 'yyyy') {\n return nextToken !== undefined && DIGIT_LEADING_TOKENS.has(nextToken) ? YYYY_EXACT : YYYY_EXTENDED;\n }\n if (token === 'y') {\n return Y_FRAGMENT;\n }\n\n const numeric = NUMERIC_FRAGMENTS[token];\n if (numeric) {\n return numeric;\n }\n\n if (token === 'QQQ') {\n return QQQ_FRAGMENT;\n }\n\n if (FORMAT_ONLY_TOKENS.has(token)) {\n throw new UnknownTokenError({\n token,\n message:\n `temporal-fmt: token \"${token}\" is format-only — it can't be parsed back into a value. ` +\n `Use a different token in the parse format string (e.g. \"d\" for \"do\", \"MM\" for \"ww\").`,\n });\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-insensitive on purpose: \"pm\"/\"Pm\"/\"PM\" all mean the same thing,\n // and unlike the Md-glue ambiguity elsewhere in this file, there's no\n // second valid reading to guess wrong — so rejecting on case buys no\n // correctness, only friction against real-world data (mixed-case CSV\n // exports, lowercase log timestamps).\n case 'a': return alternation(vocab.dayPeriod, true);\n case 'zzz': return getTimeZoneFragment();\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n return OFFSET_SHAPES[token]!;\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. tokenFragment's only caller (buildCapturingPattern\n in parsePattern.ts) always passes piece.value straight from\n tokenize.ts, which only ever emits strings from tokens.ts's TOKENS\n table. Every token in that table is handled above: the numeric\n ones via NUMERIC_FRAGMENTS, QQQ via QQQ_FRAGMENT, the format-only\n ones via the FORMAT_ONLY_TOKENS check earlier in this function, and\n everything else via one of the switch cases. There's no token\n string that can reach this default without either tokens.ts\n registering something new or tokenize.ts being bypassed, neither of\n which happens on the parse() path. */\n default:\n throw new UnknownTokenError({ token, message: `temporal-fmt: unknown token \"${token}\"` });\n /* c8 ignore stop @preserve */\n }\n}\n\n// Tokens whose fragment is variable-width (1-2 digits, no leading zero).\n// Two or more of these glued with no literal separator between them can\n// have more than one digit-split that's independently valid against every\n// fragment in the run — see the big comment on NUMERIC_FRAGMENTS above.\n// Reordering alternation branches picks a winner for *some* of these\n// cases, but can't make both directions of a pair (e.g. \"Md\" and \"dM\")\n// agree, because the ambiguity is in the input string itself, not in how\n// any one fragment is written. exported so parsePattern.ts can find runs\n// of these that need split-counting at match time instead of a single\n// fixed regex.\nexport const UNPADDED_NUMERIC_TOKENS = new Set(['M', 'd', 'H', 'h', 'm', 's']);\n\n// Every accept width for a given unpadded numeric token, as plain min/max\n// value + digit-length pairs — used to enumerate candidate splits of a\n// digit run at match time. Mirrors NUMERIC_FRAGMENTS's semantics exactly\n// (same accepted values), just as data instead of regex source, since\n// enumerating splits against a compiled regex per-candidate would be\n// slower and harder to reason about than checking numeric ranges directly.\nexport const UNPADDED_NUMERIC_RANGES: Record<string, Array<{ digits: 1 | 2; min: number; max: number }>> = {\n M: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n d: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 31 }],\n H: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 23 }],\n h: [{ digits: 1, min: 1, max: 9 }, { digits: 2, min: 10, max: 12 }],\n m: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n s: [{ digits: 1, min: 0, max: 9 }, { digits: 2, min: 10, max: 59 }],\n};\n\n/**\n * Given the literal digit string a run of N adjacent unpadded-numeric\n * tokens matched as a whole (e.g. \"112\" for a 2-token run), enumerates\n * every way to split it into N pieces (one per token, each piece 1-2\n * digits per that token's own width rule) and returns every split where\n * every piece is independently valid for its token. Length 0 means the\n * run's regex match shouldn't have been possible in the first place\n * (shouldn't happen — the caller only invokes this after the whole\n * pattern already matched, meaning at least one split exists: the one the\n * regex actually took). Length 1 means the reading is unambiguous.\n * Length 2+ means true ambiguity — the caller should throw rather than\n * pick one.\n *\n * Recursive over token count rather than hardcoded to 2, so a 3+ token\n * unseparated run (e.g. \"Hms\") is covered by the same logic without a\n * special case — those are rarer in practice but not impossible, and a\n * partial fix that only covered pairs would leave the identical bug for\n * anyone writing a 3-token glued run.\n */\nexport function enumerateValidSplits(digits: string, tokens: string[]): number[][] {\n const memo = new Map<string, number[][]>();\n\n function solve(tokenIndex: number, offset: number): number[][] {\n const key = `${tokenIndex}:${offset}`;\n const cached = memo.get(key);\n if (cached) {\n return cached;\n }\n\n if (tokenIndex === tokens.length) {\n const result = offset === digits.length ? [[]] : [];\n memo.set(key, result);\n return result;\n }\n\n const token = tokens[tokenIndex];\n const ranges = UNPADDED_NUMERIC_RANGES[token!];\n /* c8 ignore start @preserve -- defensive guard, not reachable through\n the public API. enumerateValidSplits's only caller (parse.ts, both\n call sites) passes run.tokens straight from\n pattern.ambiguousRuns, which parsePattern.ts only ever populates\n with tokens already checked against UNPADDED_NUMERIC_TOKENS — the\n exact same key set as UNPADDED_NUMERIC_RANGES. There's no path\n where a token reaches here without having already passed that\n check. */\n if (!ranges) {\n throw new Error(`temporal-fmt: internal error — \"${token}\" is not an unpadded numeric token`);\n }\n /* c8 ignore stop @preserve */\n\n const results: number[][] = [];\n for (const { digits: width, min, max } of ranges) {\n if (offset + width > digits.length) continue;\n const piece = digits.slice(offset, offset + width);\n if (width === 2 && piece[0] === '0') continue;\n const value = Number(piece);\n if (value < min || value > max) continue;\n\n for (const restSplit of solve(tokenIndex + 1, offset + width)) {\n results.push([value, ...restSplit]);\n if (results.length === 2) break;\n }\n if (results.length === 2) break;\n }\n\n memo.set(key, results);\n return results;\n }\n\n return solve(0, 0);\n}","import type { Piece } from './tokenize.js';\nimport { tokenFragment, UNPADDED_NUMERIC_TOKENS, DIGIT_LEADING_TOKENS, UNBOUNDED_WIDTH_TOKENS } from './pattern.js';\nimport { FormatSyntaxError } from './errors.js';\n\nfunction escapeRegExp(literal: string): string {\n return literal.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n// Guards against catastrophic-backtracking (ReDoS) patterns.\n//\n// Every confirmed ReDoS in this library shares one shape: two or more\n// variable-width digit-consuming regex fragments placed so the engine\n// can't tell where one ends and the next begins — either glued with no\n// separator (\"MdMdMd…\", \"HmsHms…\") or separated only by a literal that\n// itself starts with a digit (\"M1M1M1…\", \"yyyy1yyyy1…\"). Each fragment\n// then has multiple ways to divide the digit run, and a failing match\n// makes the engine explore every combination — exponential in the number\n// of fragments. Measured against the pre-fix code: \"Md\"×13 (26-char\n// format, 40-char input) ≈ 2.7 s; \"yyyy1\"×8 (48-char format) ≈ 26 s;\n// both grow roughly ×3–14 per additional fragment.\n//\n// Three structural defenses, applied at pattern-build time:\n//\n// 1. A run of 2+ glued unpadded-numeric tokens is emitted as ONE\n// bounded digit group `(?<rN>\\d{R,2R})` instead of R separate\n// variable-width fragments. The per-token split is resolved after\n// the match by enumerateValidSplits() (pattern.ts) — the exact\n// machinery parse() already used to detect ambiguous glued runs —\n// so the documented behavior (unique split resolves; 2+ valid\n// splits throws in strict mode / heuristic-picks in lenient; 0\n// splits is a mismatch) is preserved. A lone `\\d{R,2R}` group\n// backtracks at most R+1 times, which is linear.\n//\n// 2. yyyy uses the exact `-?\\d{4}` fragment not only when the next\n// *token* is digit-leading (existing rule) but also when the next\n// *literal* starts with a digit. An open-ended `-?\\d{4,}` year\n// glued to a digit literal is the cheapest exponential engine\n// there is (unbounded width choices per year), and the exact form\n// matches everything the open-ended form matched in that position\n// except years with 5+ digits glued directly to an unquoted digit\n// literal — a pathological corner deliberately traded away.\n//\n// 3. An ambiguity budget for what's left: every adjacency between a\n// variable-width digit consumer (a lone unpadded token, or a glued\n// run group) and a digit-consuming successor (a digit-leading token\n// or a literal starting with a digit) costs log2 of the consumer's\n// width choices. A pattern whose total exceeds MAX_AMBIGUITY_BITS\n// (12 — a hard ceiling of 4096 backtrack paths) is rejected at\n// build time with a FormatSyntaxError. Realistic format strings\n// score 0–3; the \"M1M1M1…\" attack scores one bit per glued pair.\nconst MAX_AMBIGUITY_BITS = 12;\n\nfunction widthChoicesBits(choices: number): number {\n return Math.ceil(Math.log2(Math.max(choices, 1)));\n}\n\n// Does this piece's regex fragment START by consuming a bare digit?\n// (Tokens whose match can begin with 0-9, and literals whose first\n// character is a digit.) Used to find the boundaries where a preceding\n// variable-width digit consumer could trade digits with a successor.\nfunction isDigitConsumingStart(piece: Piece | undefined): boolean {\n if (piece === undefined) return false;\n if (piece.kind === 'literal') return /^[0-9]/.test(piece.value);\n return DIGIT_LEADING_TOKENS.has(piece.value);\n}\n\nexport interface CapturingPattern {\n regex: RegExp;\n groups: Array<{ name: string; token: string }>; // token pieces, in order\n // Runs of 2+ adjacent unpadded-numeric tokens with no literal separator\n // between them (e.g. \"Md\", \"Hms\"). Each run is captured by ONE regex\n // group named `groupName` spanning the run's whole digit run\n // (R..2·R digits); per-token values come from enumerateValidSplits()\n // at match time. `groupNames` lists the per-token group names — they\n // appear in `groups` for structure/positions but have no counterpart\n // in the regex itself, so consumers must read their values from the\n // split enumeration, not from match.groups.\n ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }>;\n}\n\n/**\n * Same walk as buildPatternSource() in pattern.ts, but each token piece\n * gets its own named capture group (positionally named so the same token,\n * e.g. \"yyyy\", could in theory appear twice) so a caller can pull the\n * matched substring for each token back out after a successful match.\n */\nexport function buildCapturingPattern(pieces: Piece[], locale: string): CapturingPattern {\n const groups: Array<{ name: string; token: string }> = [];\n const ambiguousRuns: Array<{ groupName: string; groupNames: string[]; tokens: string[] }> = [];\n let source = '';\n let i = 0;\n let ambiguityBits = 0;\n\n // Tracks the current run of adjacent unpadded-numeric token pieces (no\n // literal or non-unpadded token has broken it yet). Names are recorded\n // in declaration order so a run of 2+ can emit one group while its\n // member tokens still get individual entries in `groups`.\n let currentRun: { names: string[]; tokens: string[] } = { names: [], tokens: [] };\n\n const flushRun = (nextPiece: Piece | undefined) => {\n if (currentRun.tokens.length === 1) {\n // A lone unpadded token: emit its normal (two-way) fragment. Two\n // width choices are harmless on their own; if the next element\n // can consume a digit, charge one ambiguity bit for the boundary.\n const name = currentRun.names[0]!;\n const token = currentRun.tokens[0]!;\n source += `(?<${name}>${tokenFragment(token, locale)})`;\n if (isDigitConsumingStart(nextPiece)) ambiguityBits += 1;\n } else if (currentRun.tokens.length >= 2) {\n // Emit the accumulated run as ONE bounded digit group. R unpadded\n // tokens accept between R and 2R digits in total; anything outside\n // that span can't match regardless of how the digits split, so the\n // single group's acceptance region is exactly the union of the old\n // per-token fragments' regions. Backtracking into this group is\n // bounded at R+1 width choices — linear, not exponential.\n const runName = `r${i++}`;\n const tokenCount = currentRun.tokens.length;\n source += `(?<${runName}>\\\\d{${tokenCount},${tokenCount * 2}})`;\n // Note: no `groups` entry for the run group itself — `groups` lists\n // per-token pieces only (its members were already pushed when\n // visited), so consumers like parseToParts see exactly one entry\n // per token, same as before this run-group change. The regex group\n // is reached through ambiguousRuns[].groupName.\n ambiguousRuns.push({\n groupName: runName,\n groupNames: currentRun.names,\n tokens: currentRun.tokens,\n });\n // A run group adjacent to a digit-consuming successor keeps its\n // (R+1) width choices at that boundary — charge the budget.\n if (isDigitConsumingStart(nextPiece)) {\n ambiguityBits += widthChoicesBits(tokenCount + 1);\n }\n }\n currentRun = { names: [], tokens: [] };\n };\n\n for (const [idx, piece] of pieces.entries()) {\n if (piece.kind === 'literal') {\n flushRun(piece);\n source += escapeRegExp(piece.value);\n continue;\n }\n\n if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n // Part of a (potential) glued run — defer fragment emission to\n // flushRun so a run of 2+ collapses into one bounded group.\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n currentRun.names.push(name);\n currentRun.tokens.push(piece.value);\n continue;\n }\n\n flushRun(piece);\n const name = `g${i++}`;\n groups.push({ name, token: piece.value });\n const nextPiece = pieces[idx + 1];\n const nextToken = nextPiece?.kind === 'token' ? nextPiece.value : undefined;\n\n // \"y\" has no bounded fallback the way yyyy does (see tokenFragment in\n // pattern.ts) — it's unpadded by definition, so there's no narrower\n // fixed-width shape to fall back to. A run of \"any number of digits\"\n // immediately next to another digit consumer has no finite ambiguity\n // score to charge against MAX_AMBIGUITY_BITS below, so this is\n // refused outright at build time instead of estimated.\n if (UNBOUNDED_WIDTH_TOKENS.has(piece.value) && isDigitConsumingStart(nextPiece)) {\n throw new FormatSyntaxError({\n reason:\n `token \"${piece.value}\" has no fixed width — it can't be placed directly next to another ` +\n `digit-reading token or a literal that starts with a digit, since there's no way to tell ` +\n `where \"${piece.value}\" ends and the next field begins. Add a non-digit separator (e.g. \"-\" or \" \") after it.`,\n });\n }\n\n // yyyy picks its fragment based on what follows: the exact 4-digit\n // form whenever a digit-consuming element comes next (digit-leading\n // token — the pre-existing rule — OR a literal starting with a\n // digit, added by the ReDoS fix; see the guard block above). The\n // open-ended form is only safe when nothing digit-consuming can\n // follow it.\n if (piece.value === 'yyyy' && nextToken === undefined && isDigitConsumingStart(nextPiece)) {\n source += `(?<${name}>${tokenFragment(piece.value, locale, 'M')})`;\n } else {\n source += `(?<${name}>${tokenFragment(piece.value, locale, nextToken)})`;\n }\n }\n flushRun(undefined);\n\n if (ambiguityBits > MAX_AMBIGUITY_BITS) {\n throw new FormatSyntaxError({\n reason:\n `format string has too many variable-width numeric tokens glued to digit-consuming neighbors ` +\n `(ambiguity score ${ambiguityBits} > ${MAX_AMBIGUITY_BITS}). ` +\n `This shape makes the regex engine backtrack exponentially on near-miss input. ` +\n `Add a non-digit separator between these tokens (e.g. \"-\" or \" \") or use their padded forms (MM/dd/HH/mm/ss).`,\n });\n }\n\n // 'd' flag enables match.indices.groups — used by parseToParts to\n // report each token's actual position in the input. Without it,\n // computing per-group positions would require a separate walk of the\n // piece list against the input, duplicating logic the regex already\n // has. Backward-compatible: 'd' only adds an `indices` property to\n // the match result, no behavioral change to the match itself.\n return { regex: new RegExp(`^(?:${source})$`, 'ud'), groups, ambiguousRuns };\n}","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { buildCapturingPattern, type CapturingPattern } from './parsePattern.js';\nimport { enumerateValidSplits, isValidTimeZone } from './pattern.js';\nimport { getLocaleVocab, canonicalCacheKey, assertValidLocaleTag, subscribeToVocabChanges } from './localeVocab.js';\nimport { getTemporal } from './temporalProvider.js';\nimport { MAX_FORMAT_LENGTH, MAX_INPUT_LENGTH } from './constants.js';\nimport { TemporalFmtError, InvalidTimeZoneError, InvalidOffsetError, FormatSyntaxError, ParseMismatchError, AmbiguousInputError, InvalidDateError, wrapUntypedError } from './errors.js';\nimport { applyParseNumbering, type NumberingParseOptions } from './numbering.js';\n\n// format strings are short hand-written literals reused across many calls —\n// cache the compiled capturing pattern per (formatStr, locale) pair instead\n// of rebuilding it every call.\nconst patternCache = new Map<string, CapturingPattern>();\nconst MAX_CACHE_SIZE = 500;\n\n// A compiled pattern embeds locale-vocabulary alternations (MMMM/MMM/\n// EEEE/EEE/a fragments) at build time. When registerLocaleVocab()\n// swaps a locale's vocabulary, every cached pattern for any locale\n// becomes potentially stale — the cached regex would keep matching the\n// OLD vocabulary while format() renders the new one, breaking the\n// format→parse round-trip for the library's own output. Clear the whole\n// cache on any vocab change: rebuilds are cheap and self-limiting via\n// the cache size cap.\nsubscribeToVocabChanges(() => { patternCache.clear(); });\n\nfunction getPattern(formatStr: string, locale: string): CapturingPattern {\n const key = JSON.stringify([canonicalCacheKey(locale), 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 pattern = buildCapturingPattern(tokenize(formatStr), locale);\n patternCache.set(key, pattern);\n return pattern;\n}\n\n// Requires an explicit `-u-ca-` extension (e.g. 'en-u-ca-hebrew') to apply\n// a non-Gregorian calendar, per parse()'s own docstring. 'gregory' counts\n// as \"no calendar\" so the default locale keeps constructing plain ISO 8601.\n//\n// Used to key off resolvedOptions().calendar instead — a locale's\n// *default* calendar, whether the caller asked for one or not. That broke\n// th-TH silently: its default is 'buddhist', so plain Gregorian-looking\n// digits parsed 543 years off, while format() has no matching calendar\n// step and just prints the object's own ISO fields either way.\nconst calendarCache = new Map<string, string | undefined>();\nconst MAX_CALENDAR_CACHE_SIZE = 500;\n\nfunction resolveCalendar(locale: string): string | undefined {\n // Typed validation first: a genuinely malformed tag (bare private-use\n // singleton, control characters, garbage) surfaces as the library's\n // InvalidLocaleError instead of a raw engine RangeError — same code as\n // before, different (documented, structured) error class.\n assertValidLocaleTag(locale);\n // canonicalCacheKey is memoized (localeVocab.ts), so the common path —\n // repeated parse() calls with the same locale — no longer constructs a\n // fresh Intl.Locale per call just to compute the calendar-cache key.\n const canonicalLocale = canonicalCacheKey(locale);\n if (calendarCache.has(canonicalLocale)) {\n return calendarCache.get(canonicalLocale);\n }\n if (calendarCache.size >= MAX_CALENDAR_CACHE_SIZE) {\n const oldestKey = calendarCache.keys().next().value;\n if (oldestKey !== undefined) calendarCache.delete(oldestKey);\n }\n let calendar: string | undefined;\n const parts = canonicalLocale.split('-');\n const extensionIndex = parts.indexOf('u');\n const calendarKeyIndex = extensionIndex === -1 ? -1 : parts.indexOf('ca', extensionIndex + 1);\n if (calendarKeyIndex !== -1 && calendarKeyIndex + 1 < parts.length) {\n const resolved = new Intl.DateTimeFormat(canonicalLocale).resolvedOptions().calendar;\n calendar = resolved === 'gregory' ? undefined : resolved;\n }\n calendarCache.set(canonicalLocale, calendar);\n return calendar;\n}\n\ninterface Fields {\n year?: number;\n twoDigitYear?: number;\n month?: number;\n day?: number;\n hour?: number;\n hour12?: number;\n dayPeriodRaw?: string;\n isPM?: boolean;\n minute?: number;\n second?: number;\n millisecond?: number;\n microsecond?: number;\n nanosecond?: number;\n timeZoneId?: string;\n // Canonical `+HH:MM` form of any offset token (X/XX/XXX/x/xx/xxx)\n // captured in this pattern. Distinct from timeZoneId because the two\n // can coexist in the same pattern (e.g. \"yyyy-MM-dd HH:mm zzz XXX\") —\n // see the cross-check after construction for how a mismatch between\n // them is resolved.\n offsetString?: string;\n weekdayExpected?: number;\n weekdayRaw?: string;\n quarter?: number;\n}\n\n// Normalizes a captured offset-token string into the canonical `+HH:MM`\n// shape Temporal.ZonedDateTime.from accepts as a `timeZone` value. Throws\n// descriptive errors for out-of-range hours/minutes, since the regex\n// shape (OFFSET_SHAPES in pattern.ts) is deliberately permissive — a\n// post-match range check here gives the user a specific error (\"offset\n// hours 99 out of range, max 14\") instead of \"no valid pattern matches\".\n//\n// Range bounds: -12:00 to +14:00, the standard IANA offset range\n// (Baker/Howland at -12, Kiritimati at +14). +14:01 / -12:01 etc. are\n// rejected explicitly even though the per-piece bounds (hours ≤ 14,\n// minutes ≤ 59) alone wouldn't catch them.\nfunction parseOffsetString(raw: string, token: string): string {\n if (raw === 'Z') {\n /* c8 ignore start @preserve -- unreachable: lowercase tokens' regex\n (OFFSET_SHAPES in pattern.ts) has no \"Z\" alternative at all, so\n raw === 'Z' can only ever be reached when token is one of the\n uppercase variants (X/XX/XXX). A lowercase token can't even\n capture \"Z\" as `raw` in the first place. */\n if (token === 'x' || token === 'xx' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" doesn't accept \"Z\" — only the uppercase variants (X/XX/XXX) emit \"Z\" for UTC. ` +\n `Use \"+00:00\", \"+0000\", or \"+00\" depending on the variant's width.`\n );\n }\n /* c8 ignore stop @preserve */\n return '+00:00';\n }\n\n const sign = raw[0];\n /* c8 ignore start @preserve -- unreachable: raw is a regex-captured\n group from an offset token, and every OFFSET_SHAPES pattern\n (pattern.ts) is anchored to either \"Z\" or a leading [+-]. raw's\n first character can never be anything else by the time it reaches\n this function. */\n if (sign !== '+' && sign !== '-') {\n throw new Error(`temporal-fmt: offset \"${raw}\" for token \"${token}\" doesn't start with \"+\", \"-\", or \"Z\".`);\n }\n /* c8 ignore stop @preserve */\n const body = raw.slice(1);\n let hoursStr: string;\n let minutesStr: string;\n if (body.length === 2) {\n /* c8 ignore start @preserve -- unreachable: each offset token's own\n regex shape in OFFSET_SHAPES (pattern.ts) already gates which\n body shapes it can capture. Only X and x ever match a 2-digit\n body — XX/xx/XXX/xxx's regexes can't produce one — so this\n mismatch can never actually fire through parse(). */\n // +HH — only X/x emit this shape; XX/xx/XXX/xxx always carry minutes.\n if (token !== 'X' && token !== 'x') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires minutes, but \"${raw}\" has none.`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body;\n minutesStr = '00';\n } else if (body.length === 4) {\n /* c8 ignore start @preserve -- unreachable, same reason as the\n 2-digit case above: XXX/xxx's regex requires a colon, so it can\n never capture a 4-digit no-colon body in the first place. */\n // +HHMM — X/x (when minutes are non-zero) or XX/xx.\n if (token === 'XXX' || token === 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it requires a colon between hours and minutes (e.g. \"${sign}${body.slice(0, 2)}:${body.slice(2)}\").`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(2, 4);\n } else if (body.length === 5 && body[2] === ':') {\n /* c8 ignore start @preserve -- unreachable, same reason again: only\n XXX/xxx's regex can produce a colon-shaped body; X/x/XX/xx never\n capture one. */\n // +HH:MM — XXX/xxx only.\n if (token !== 'XXX' && token !== 'xxx') {\n throw new Error(\n `temporal-fmt: offset token \"${token}\" can't match \"${raw}\" — it doesn't use a colon (use \"${sign}${body.slice(0, 2)}${body.slice(3)}\" instead).`\n );\n }\n /* c8 ignore stop @preserve */\n hoursStr = body.slice(0, 2);\n minutesStr = body.slice(3, 5);\n /* c8 ignore start @preserve -- unreachable: every offset token's\n regex only ever produces a body of length 2, length 4, or length 5\n with a colon at index 2 (see OFFSET_SHAPES in pattern.ts) — no\n shape falls outside those three cases, so this else arm can't be\n taken through parse(). Kept as an exhaustiveness fallback so\n hoursStr/minutesStr are assigned on every path TypeScript can see. */\n } else {\n throw new Error(`temporal-fmt: offset \"${raw}\" doesn't match the shape token \"${token}\" accepts.`);\n }\n /* c8 ignore stop @preserve */\n\n const hours = Number(hoursStr);\n const minutes = Number(minutesStr);\n // Per-piece range checks catch most malformed input.\n if (hours > 14) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset hours ${hours} in \"${raw}\" out of range (max 14 — Kiritimati, Line Islands is +14:00).`,\n });\n }\n if (minutes > 59) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset minutes ${minutes} in \"${raw}\" out of range (max 59).`,\n });\n }\n // Boundary: +14:01..+14:59 and -12:01..-12:59 are out of range even\n // though each piece alone is in bounds — the overall offset exceeds\n // the IANA-supported range.\n if (sign === '+' && hours === 14 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported UTC offset of +14:00.`,\n });\n }\n if (sign === '-' && hours === 12 && minutes !== 0) {\n throw new InvalidOffsetError({\n actual: raw,\n message: `temporal-fmt: offset \"${raw}\" exceeds the maximum supported negative UTC offset of -12:00.`,\n });\n }\n return `${sign}${hoursStr}:${minutesStr}`;\n}\n\nfunction assignField<T>(fields: Fields, key: keyof Fields, value: T): void {\n (fields as Record<string, T | undefined>)[key] = value;\n}\n\nfunction applyGroup(fields: Fields, token: string, raw: string, locale: string, formatStr: string): void {\n const vocab = getLocaleVocab(locale);\n switch (token) {\n case 'yyyy': case 'y':\n assignField(fields, 'year', Number(raw));\n break;\n case 'yy':\n assignField(fields, 'twoDigitYear', Number(raw));\n break;\n case 'MM': case 'M':\n assignField(fields, 'month', Number(raw));\n break;\n case 'MMMM':\n assignField(fields, 'month', vocab.monthLong.indexOf(raw) + 1);\n break;\n case 'MMM':\n assignField(fields, 'month', vocab.monthShort.indexOf(raw) + 1);\n break;\n case 'dd': case 'd':\n assignField(fields, 'day', Number(raw));\n break;\n case 'EEEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayLong.indexOf(raw) + 1);\n break;\n case 'EEE':\n assignField(fields, 'weekdayRaw', raw);\n assignField(fields, 'weekdayExpected', vocab.weekdayShort.indexOf(raw) + 1);\n break;\n case 'HH': case 'H':\n assignField(fields, 'hour', Number(raw));\n break;\n case 'hh': case 'h':\n assignField(fields, 'hour12', Number(raw));\n break;\n case 'mm': case 'm':\n assignField(fields, 'minute', Number(raw));\n break;\n case 'ss': case 's':\n assignField(fields, 'second', Number(raw));\n break;\n case 'S': case 'SS': case 'SSS': case 'SSSS': case 'SSSSS':\n case 'SSSSSS': case 'SSSSSSS': case 'SSSSSSSS': case 'SSSSSSSSS': {\n // The captured digits are the leading N digits of a nanosecond-of-second\n // value, not the whole thing — \"5\" under SSSSSSSSS means 500000000ns\n // (half a second), not 5ns. Right-padding to 9 digits before splitting\n // is what makes that work; left-padding (or just Number(raw)) would\n // read \"5\" as 5ns instead.\n const nanoOfSecond = Number(raw.padEnd(9, '0'));\n assignField(fields, 'millisecond', Math.floor(nanoOfSecond / 1_000_000));\n assignField(fields, 'microsecond', Math.floor(nanoOfSecond / 1_000) % 1_000);\n assignField(fields, 'nanosecond', nanoOfSecond % 1_000);\n break;\n }\n case 'a': {\n // Matches case-insensitively (see pattern.ts's foldCase), so the\n // lookup here has to fold too, or \"pm\" would pass the regex and\n // then fail this indexOf against the exact-case vocab.\n const periodIndex = vocab.dayPeriod.findIndex((p) => p.toLowerCase() === raw.toLowerCase());\n /* c8 ignore start @preserve -- unreachable: the 'a' token's regex\n fragment (pattern.ts's alternation() over vocab.dayPeriod) can\n only ever capture a case-insensitive match of one of\n vocab.dayPeriod's own entries. Both the regex and this lookup\n derive their vocab from the same `locale` via getLocaleVocab(),\n so periodIndex can't come back negative through parse(). */\n if (periodIndex < 0) throw new Error(`temporal-fmt: unknown day period \"${raw}\" for locale \"${locale}\".`);\n /* c8 ignore stop @preserve */\n assignField(fields, 'dayPeriodRaw', raw);\n assignField(fields, 'isPM', periodIndex === 1);\n break;\n }\n case 'zzz':\n assignField(fields, 'timeZoneId', raw);\n break;\n case 'X': case 'XX': case 'XXX':\n case 'x': case 'xx': case 'xxx':\n assignField(fields, 'offsetString', parseOffsetString(raw, token));\n break;\n case 'Q':\n assignField(fields, 'quarter', Number(raw));\n break;\n case 'QQQ':\n // strips the literal \"Q\" prefix the token itself formats; the suffix\n // digit is the quarter value 1-4\n assignField(fields, 'quarter', Number(raw.slice(1)));\n break;\n }\n}\n\n// The lenient split-selection heuristic for ambiguous glued numeric runs.\n// See README \"Lenient parse mode\" — the strict default throws on these,\n// lenient mode opts into picking one split instead.\nfunction pickLenientSplit(splits: number[][], tokens: string[]): number[] {\n // Prefer the split where a \"d\" (day) token, if any, has a value of 12 or\n // less. Rationale: when a person writes a glued run like \"121\" for an\n // Md format string, the reading \"Dec 1\" (M=12, d=1) is what they\n // typically meant — if they meant \"Jan 21\" they would more often have\n // written it as \"1/21\" or \"01/21\" with a separator or padding, since the\n // 2-digit day is the more naturally-cohesive unit to keep glued. This\n // isn't a guarantee, which is exactly why lenient mode is opt-in — but\n // it's a reasonable default when the caller has asked us to guess.\n const dayIndex = tokens.indexOf('d');\n if (dayIndex !== -1) {\n const smallDaySplits = splits.filter((s) => s[dayIndex]! <= 12);\n if (smallDaySplits.length > 0) {\n return smallDaySplits[0]!;\n }\n }\n // Fallback to the first valid split when the day heuristic doesn't\n // narrow it down — deterministic, and \"first\" here means \"whichever\n // enumerateValidSplits returned first\", which is a depth-first\n // leftmost-shortest walk over the candidate splits.\n return splits[0]!;\n}\n\n// emulates strptime (POSIX) for 2-digit years so the result doesn't depend\n// on the current clock: 00-68 -> 2000-2068, 69-99 -> 1900-1999\n// https://www.man7.org/linux//man-pages/man3/strptime.3p.html\nfunction resolveYear(fields: Fields): number | undefined {\n if (fields.year !== undefined && fields.twoDigitYear !== undefined) {\n // FormatSyntaxError, not ParseMismatchError: this is a contradiction\n // in the format string itself (both a full-year token and \"yy\" are\n // present), not a mismatch between the format and a specific input.\n // \"y\" and \"yyyy\" both land in fields.year, so this also covers a\n // \"y\"+\"yy\" mix, not just \"yyyy\"+\"yy\" — kept generic rather than\n // naming a specific pair.\n throw new FormatSyntaxError({\n message: 'temporal-fmt: format string mixes a full-year token (\"yyyy\"/\"y\") with the two-digit \"yy\" token.',\n });\n }\n if (fields.year !== undefined) return fields.year;\n if (fields.twoDigitYear !== undefined) {\n return fields.twoDigitYear <= 68 ? 2000 + fields.twoDigitYear : 1900 + fields.twoDigitYear;\n }\n return undefined;\n}\n\nfunction resolveHour(fields: Fields, formatStr: string, locale: string): number | undefined {\n if (fields.hour !== undefined && fields.hour12 !== undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n `12-hour token (\"hh\"/\"h\").`,\n });\n }\n if (fields.hour !== undefined) {\n if (fields.dayPeriodRaw !== undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" mixes a 24-hour token (\"HH\"/\"H\") with a ` +\n `day-period token (\"a\"). Both describe the same field; use one or the other, not both.`,\n });\n }\n return fields.hour;\n }\n if (fields.hour12 !== undefined) {\n if (fields.isPM === undefined) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" uses a 12-hour token (\"hh\"/\"h\") without an \"a\" token, ` +\n `so parse() can't tell AM from PM.`,\n });\n }\n return (fields.hour12 % 12) + (fields.isPM ? 12 : 0);\n }\n return undefined;\n}\n\n/**\n * Parses `input` against `formatStr` and builds the real Temporal value it\n * describes: a `Temporal.PlainDate`, `PlainTime`, `PlainDateTime`, or\n * `ZonedDateTime` depending on which tokens are present.\n *\n * Returns `unknown` — this package has no ambient `Temporal` types to return\n * a real one against.\n *\n * `options.locale` picks the calendar the result is built in. Pass a locale\n * tag with a `-u-ca-` extension (e.g. `'en-u-ca-hebrew'`) to parse into a\n * non-Gregorian calendar.\n *\n * @throws if `input` doesn't match `formatStr`'s shape at all\n * @throws if it matches the shape but describes an impossible date (e.g. Feb\n * 30) or self-contradictory data (e.g. a weekday name that doesn't match the\n * actual date)\n *\n * @example\n * parse('yyyy-MM-dd HH:mm', '2026-08-04 15:45') // Temporal.PlainDateTime\n * parse('yyyy-MM', '2026-08-04T15:45:30') // throws — shape doesn't match\n * parse('yyyy-MM-dd', '2026-02-30') // throws — not a real date\n */\nexport function parse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Transliterate non-ASCII numerals to ASCII before any matching happens,\n // when the caller opts in via parseNumberingSystem. Every regex this\n // module builds expects 0-9; this is the one place that assumption\n // could otherwise be violated by locale-native input digits.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const calendar = resolveCalendar(locale);\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n\n // The regex's zzz fragment only matches a bounded zone-id *shape* (see\n // TIME_ZONE_SHAPE in pattern.ts) rather than alternating every real IANA\n // name inline, so a shape match isn't proof of a real zone yet — check\n // Zone ids can't be enumerated in the regex itself (there are ~400 of\n // them and they change over time as IANA updates the tz database), so\n // the regex only captures the zzz group's shape and this loop checks\n // each captured zzz group against the actual zone list here. Unlike a\n // regex-shape mismatch, this failure has a specific cause worth\n // naming: the shape matched but the zone id itself isn't recognized.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // A run of 2+ adjacent unpadded-numeric tokens with no literal separator\n // (e.g. \"Md\", \"dM\", \"Hms\") is captured by a single bounded digit group in\n // the regex (see buildCapturingPattern — per-token variable-width\n // fragments made near-miss matching exponential in the number of glued\n // tokens). Resolve each run's per-token split here instead: unique valid\n // split resolves (identical to what the old regex's own greedy match\n // produced, since the old match was always one of the valid splits);\n // 2+ valid splits is genuinely ambiguous input — strict mode throws,\n // lenient opts into the documented heuristic. A span with 0 valid\n // splits matched the run's width window but names no valid per-token\n // assignment, which the old per-token fragments rejected at match\n // time — surface the same \"no valid pattern matches\" error.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n // Strict default — throw on ambiguity. The whole point of the\n // library's parse() is to refuse to guess when the same input has\n // more than one valid reading. Lenient mode (opt-in via\n // options.lenient) instead picks one split via a documented\n // heuristic — see pickLenientSplit() above and the README section\n // \"Lenient parse mode\" for why this is strictly additive and never\n // the default.\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n const fields: Fields = {};\n // Per-token values for glued-run members come from the split\n // enumeration above (runValues); every other token reads its own\n // regex group directly.\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n applyGroup(fields, token, raw, locale, formatStr);\n }\n\n const year = resolveYear(fields);\n const hour = resolveHour(fields, formatStr, locale);\n const { month, day, minute, second, millisecond, microsecond, nanosecond, timeZoneId, offsetString, weekdayExpected, weekdayRaw, quarter } = fields;\n\n const hasAnyDatePart = year !== undefined || month !== undefined || day !== undefined;\n const hasFullDate = year !== undefined && month !== undefined && day !== undefined;\n if (hasAnyDatePart && !hasFullDate) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an incomplete date — ` +\n `year, month, and day tokens must all be present together.`,\n });\n }\n\n const hasTime = hour !== undefined || minute !== undefined || second !== undefined || millisecond !== undefined;\n\n if (timeZoneId !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a \"zzz\" token but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n // Mirror zzz's full-date-and-time requirement: an offset alone is\n // meaningless without a wall-clock instant to anchor it to. Throws the\n // same kind of \"needs full date and time\" error zzz throws — separate\n // message so a caller reading it can tell which token type they\n // forgot to pair with a full date+time.\n if (offsetString !== undefined && !(hasFullDate && hasTime)) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has an offset token (X/XX/XXX/x/xx/xxx) but needs a full date and time ` +\n `to build a ZonedDateTime.`,\n });\n }\n\n if (weekdayExpected !== undefined && !hasFullDate) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" has a weekday token (\"EEEE\"/\"EEE\") but needs ` +\n `a full date to validate it against.`,\n });\n }\n\n if (!hasFullDate && !hasTime) {\n // shouldn't happen — every token maps to a date, time, zone, or\n // weekday field, and weekday-without-date already threw above\n throw new FormatSyntaxError({\n format: formatStr,\n message: `temporal-fmt: format string \"${formatStr}\" has no date or time tokens to parse.`,\n });\n }\n\n const temporal = getTemporal();\n const timeFields = {\n hour: hour ?? 0,\n minute: minute ?? 0,\n second: second ?? 0,\n millisecond: millisecond ?? 0,\n microsecond: microsecond ?? 0,\n nanosecond: nanosecond ?? 0,\n };\n // omitted entirely for the default calendar (see resolveCalendar) so\n // construction stays plain ISO 8601 unless a caller's locale asks for\n // something else — Temporal calendars don't apply to time-only values.\n const calendarField = calendar ? { calendar } : {};\n\n // overflow: 'reject' — without it Temporal *clamps* out-of-range fields\n // (Feb 30 silently becomes Feb 28) instead of throwing, which would\n // contradict the \"throws on genuinely invalid data\" behavior parse() promises.\n const reject = { overflow: 'reject' as const };\n\n let result: unknown;\n try {\n if (timeZoneId !== undefined) {\n // offset: 'prefer' never throws on a mismatch — it just falls back to\n // the zone's real offset at this instant, silently overriding\n // whatever the offset token said. That's also what resolves a\n // repeated wall-clock time (DST fall-back): without an explicit\n // offset, Temporal defaults to the first occurrence, so passing the\n // token's offset here is what lets a second-occurrence input resolve\n // to the second occurrence instead of always falling back to the\n // first. Either way, \"prefer\" can't be used to detect disagreement —\n // that's checked explicitly below, once we have a real ZonedDateTime\n // to compare against, instead of relying on the wording of whatever\n // error Temporal's active implementation happens to throw (that\n // wording isn't part of the spec and differs between the native\n // Temporal global and userland polyfills).\n const zoneOptions: Temporal.ZonedDateTimeFromOptions = { overflow: 'reject', offset: 'prefer' };\n result = temporal.ZonedDateTime.from(\n {\n year: year!, month: month!, day: day!, ...timeFields, ...calendarField,\n timeZone: timeZoneId,\n ...(offsetString !== undefined ? { offset: offsetString } : {}),\n },\n zoneOptions\n );\n if (offsetString !== undefined) {\n // 'prefer' silently rewrites the wall-clock time itself when the\n // input falls in a DST gap (the time never occurred, so there's\n // no instant to prefer toward) — it doesn't just pick a\n // different offset for the same clock time, the way it does for\n // an overlap. Checking offsetString alone can't tell \"gap,\n // silently moved\" apart from \"overlap, correctly resolved,\"\n // since both can produce an actualOffset that differs from what\n // was parsed. Comparing the wall-clock fields catches the gap\n // case: they can only drift from the parsed input if Temporal\n // moved the clock time to escape the gap.\n //\n // Only checked when an offset token was given: with no offset\n // token to disagree with, a gap shifting forward is the\n // documented, wanted behavior (there's nothing to reject against).\n const zdt = result as Temporal.ZonedDateTime;\n const wallClockShifted =\n zdt.hour !== timeFields.hour ||\n zdt.minute !== timeFields.minute ||\n zdt.second !== timeFields.second;\n if (wallClockShifted) {\n // InvalidDateError, not AmbiguousInputError: the message\n // *mentions* \"not an ambiguous... instant\" as a negation, which\n // is a false-positive match against wrapUntypedError's\n // /ambiguous/i classifier regex — the actual failure here is a\n // wall-clock time that doesn't exist (DST gap), which is an\n // invalid-value problem, not an ambiguous-reading-of-input\n // problem the way the numeric-token-glue ambiguity above is.\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `\"${timeZoneId}\" has no such wall-clock time on this date — it falls in a DST gap, ` +\n `not an ambiguous or valid instant.`,\n });\n }\n const actualOffset = zdt.offset;\n if (actualOffset !== offsetString) {\n throw new ParseMismatchError({\n input, format: formatStr,\n message:\n `has both a \"zzz\" zone (${timeZoneId}) and an offset token (${offsetString}), ` +\n `but the zone's actual offset at this date/time is ${actualOffset}, not ${offsetString}.`,\n });\n }\n }\n } else if (offsetString !== undefined) {\n // An offset token with no \"zzz\" zone token present. Previously this\n // built a fixed-offset ZonedDateTime directly from the offset\n // string; now it's refused outright — an offset identifies a\n // moment's distance from UTC, not a zone, and building a\n // ZonedDateTime without a real zone identity papers over that\n // difference instead of surfacing it.\n throw new ParseMismatchError({\n input, format: formatStr,\n message:\n `format string \"${formatStr}\" has an offset token but no \"zzz\" zone token. ` +\n `An offset does not identify a time zone by itself — add \"zzz\" to the pattern, ` +\n `or parse into a PlainDateTime/PlainDate/PlainTime if a zone isn't needed.`,\n });\n } else if (hasFullDate && hasTime) {\n result = temporal.PlainDateTime.from({ year: year!, month: month!, day: day!, ...timeFields, ...calendarField }, reject);\n } else if (hasFullDate) {\n result = temporal.PlainDate.from({ year: year!, month: month!, day: day!, ...calendarField }, reject);\n } else {\n result = temporal.PlainTime.from(timeFields, reject);\n }\n } catch (err) {\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${input}\" doesn't describe a valid date/time for format \"${formatStr}\": ` +\n `${(err as Error).message}`,\n });\n }\n\n if (weekdayExpected !== undefined) {\n const actual = (result as { dayOfWeek: number }).dayOfWeek;\n if (actual !== weekdayExpected) {\n const vocab = getLocaleVocab(locale);\n throw new InvalidDateError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${weekdayRaw}\" doesn't match the actual weekday (${vocab.weekdayLong[actual - 1]}) ` +\n `for the parsed date.`,\n });\n }\n }\n\n // Q/QQQ is a derived field of the month: 1-3 -> Q1, 4-6 -> Q2, 7-9 -> Q3,\n // 10-12 -> Q4. If a format string carries a quarter token alongside\n // month/date tokens, parse() cross-checks the parsed quarter against the\n // month the same way EEEE cross-checks weekday against date — silently\n // accepting a mismatch would defeat the point of having a quarter token\n // at all, since you'd be telling parse() one thing and the date another.\n if (quarter !== undefined && month !== undefined) {\n const expectedQuarter = Math.ceil(month / 3);\n if (quarter !== expectedQuarter) {\n throw new InvalidDateError({\n format: formatStr,\n message:\n `temporal-fmt: format string \"${formatStr}\" contains a quarter token (Q/QQQ) whose value ` +\n `(Q${quarter}) disagrees with the parsed month's actual quarter — month ${month} is in ` +\n `Q${expectedQuarter}.`,\n });\n }\n }\n\n return result;\n}\n\n// safeParse: returns a discriminated union instead of throwing. The\n// happy path returns `{ ok: true, value }` with the Temporal instance\n// (typed as `unknown` since this package has no ambient Temporal types).\n// The error path returns `{ ok: false, error }` where `error` is a\n// `TemporalFmtError` subclass when the failure is one the typed-error\n// surface in errors.ts knows how to classify (most of them), or a\n// wrapped plain `Error` (still inside a TemporalFmtError shell) when\n// the throw site hasn't been migrated yet. Callers needing the original\n// thrown object for backward compatibility should use parse() directly.\nexport type SafeParseResult =\n | { ok: true; value: unknown }\n | { ok: false; error: TemporalFmtError };\n\nexport function safeParse(formatStr: string, input: string, options: NumberingParseOptions = {}): SafeParseResult {\n try {\n return { ok: true, value: parse(formatStr, input, options) };\n } catch (err) {\n // Pass through typed errors unchanged — preserves the structured\n // fields (code/token/position/etc.) the existing typed-error\n // surface already populated.\n //\n // Every reachable throw site in parse()'s call graph now throws a\n // TemporalFmtError directly, so this condition is always true in the\n // current test suite; the false path (falling through to\n // wrapUntypedError below) is kept as a safety net for any throw site\n // added later without being migrated to a typed class immediately —\n // removing it would silently break the \"safeParse always returns a\n // TemporalFmtError\" contract documented above. Ignoring the whole\n // if/else as one unit is harmless for the true branch (real tests\n // still execute and count it — c8 just excludes it from the\n // denominator), and it's the only shape that actually suppresses the\n // false-branch marker, since c8 has no standalone \"ignore else\".\n /* c8 ignore start @preserve */\n if (err instanceof TemporalFmtError) {\n return { ok: false, error: err };\n }\n return { ok: false, error: wrapUntypedError(err as Error, { input, format: formatStr }) };\n }\n /* c8 ignore stop @preserve */\n}\n\n// tryParse: best-effort variant. Returns the parsed value or undefined.\n// Suppresses diagnostics entirely — when callers need the reason for\n// a failure, they should use safeParse(). Intentionally loose on the\n// return type (unknown) since this package has no ambient Temporal\n// types to return a real one against.\nexport function tryParse(formatStr: string, input: string, options: NumberingParseOptions = {}): unknown | undefined {\n try {\n return parse(formatStr, input, options);\n } catch {\n return undefined;\n }\n}\n\n// parseToParts: returns the matched groups with token labels, before\n// any Temporal construction. Useful for callers that want to inspect\n// what each token captured (e.g. to build a non-Temporal result, or to\n// cross-check fields themselves) without committing to the inferred\n// Temporal type parse() would build.\n//\n// Throws the same errors parse() throws for early validation (unknown\n// token, unterminated quote, no-match, ambiguity in strict mode) since\n// those failures happen before any group assignment. Construction-time\n// errors (Feb 30, weekday mismatch, etc.) do not happen here —\n// parseToParts doesn't construct anything, so it can't fail at that step.\nexport interface ParsedPart {\n token: string;\n raw: string;\n // Field name (year/month/day/...) this token would assign if handed\n // to parse()'s applyGroup loop. undefined for tokens that don't map\n // to a single field (none today, but kept here so future additions\n // don't have to widen the type).\n field?: string;\n // Position of `raw` in `input`, 0-indexed. Lets a caller highlight\n // the matched span in an editor/CLI.\n position: number;\n}\n\nexport function parseToParts(formatStr: string, input: string, options: NumberingParseOptions = {}): ParsedPart[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n if (input.length > MAX_INPUT_LENGTH) {\n throw new FormatSyntaxError({\n input,\n message: `temporal-fmt: input exceeds maximum length of ${MAX_INPUT_LENGTH} characters (got ${input.length}).`,\n });\n }\n\n // Same numeral transliteration parse() does — see the comment there.\n if (options.parseNumberingSystem) {\n input = applyParseNumbering(input, options);\n }\n\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n const match = pattern.regex.exec(input);\n if (!match) {\n throw new ParseMismatchError({\n input, format: formatStr,\n // reason set explicitly (not just message): parse.test.js's\n // \"safeParse: failure returns { ok: false, error } with a\n // TemporalFmtError\" asserts result.error.reason carries the\n // underlying detail text, matching what wrapUntypedError used to\n // populate when this message reached it as a caught plain Error.\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (pattern.groups.length === 0) {\n // ParseMismatchError, not FormatSyntaxError: pinned by\n // errors.test.js's \"format string with no tokens at all falls\n // through to ParseMismatchError\" — wrapUntypedError's classifier\n // has no branch for this message, so it lands on the\n // PARSE_MISMATCH fallback, and a direct throw here needs to\n // agree with that.\n throw new ParseMismatchError({\n format: formatStr,\n // reason set explicitly: errors.test.js's \"format string with no\n // tokens at all falls through to ParseMismatchError\" asserts\n // error.reason matches /has no tokens/.\n reason: `format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n message: `temporal-fmt: format string \"${formatStr}\" has no tokens — nothing to parse into a value.`,\n });\n }\n // Same zzz shape-validation parse() does — kept here for parity, so\n // a caller using parseToParts sees the same InvalidTimeZoneError for\n // a bogus zone id, not a silently-accepted bogus zone.\n for (const { name, token } of pattern.groups) {\n if (token === 'zzz' && !isValidTimeZone(match.groups![name]!)) {\n throw new InvalidTimeZoneError({\n input, format: formatStr, actual: match.groups![name],\n reason: 'not a recognized IANA time zone identifier',\n });\n }\n }\n\n // Glued-run split handling: same as parse() — each run's single regex\n // group is split into per-token values by enumerateValidSplits();\n // unique split resolves, 2+ splits throws in strict mode / heuristic-\n // picks in lenient, 0 splits is a shape mismatch. parseToParts mirrors\n // parse() so callers switching between the two on the same input get\n // consistent results.\n const runValues = new Map<string, string>();\n for (const run of pattern.ambiguousRuns) {\n const runDigits = match.groups![run.groupName]!;\n const splits = enumerateValidSplits(runDigits, run.tokens);\n if (splits.length === 0) {\n throw new ParseMismatchError({\n input, format: formatStr,\n reason: 'no valid pattern matches the format string and input shape',\n message: `temporal-fmt: no valid pattern matches the format string and input shape`,\n });\n }\n if (splits.length > 1) {\n if (!options.lenient) {\n throw new AmbiguousInputError({\n input, format: formatStr,\n message:\n `temporal-fmt: \"${runDigits}\" in format string \"${formatStr}\" is ambiguous — ` +\n `${splits.length} different ways to read tokens \"${run.tokens.join('')}\" (with no separator ` +\n `between them) are all individually valid (e.g. ${JSON.stringify(splits[0])} vs ${JSON.stringify(splits[1])}). ` +\n `parse() won't guess; add a separator between these tokens, or use their padded form ` +\n `(e.g. \"MM\" instead of \"M\") so each one has a fixed width. ` +\n `Pass { lenient: true } to opt into a documented heuristic that picks one.`,\n });\n }\n const picked = pickLenientSplit(splits, run.tokens);\n run.groupNames.forEach((name, idx) => runValues.set(name, String(picked[idx])));\n } else {\n run.groupNames.forEach((name, idx) => runValues.set(name, String(splits[0]![idx])));\n }\n }\n\n const parts: ParsedPart[] = [];\n // match.indices.groups (provided by the regex 'd' flag) gives the\n // [start, end] of each named group in the input. Used here so positions\n // are accurate even when literals separate tokens — summing raw\n // lengths alone wouldn't account for the literal characters between\n // groups. Falls back to the cumulative-raw-length heuristic on engines\n // without 'd' support (none we target, but the fallback keeps the\n // code robust if the flag is ever removed).\n const indices = (match as RegExpMatchArray & { indices?: { groups?: Record<string, [number, number]> } }).indices;\n const groupIndices = indices?.groups;\n let consumed = 0;\n for (const { name, token } of pattern.groups) {\n const raw = runValues.get(name) ?? match.groups![name]!;\n // Glued-run members have no regex group of their own — the regex's\n // recorded indices point at the run's overall span, not the\n // individual token's slice within it — so fall back to cumulative-\n // raw-length for them. Positions stay monotonic but may not be\n // exact for tokens inside a resolved run. Documented as a known\n // limitation; the alternative (re-running the regex with the chosen\n // split baked in) would mean a second match pass for a corner case\n // the caller opted into by gluing unpadded tokens.\n const fromIndices = !runValues.has(name) && groupIndices?.[name];\n /* c8 ignore next */\n const position = fromIndices ? fromIndices[0] : (match.index ?? 0) + consumed;\n parts.push({ token, raw, position });\n consumed += raw.length;\n }\n return parts;\n}\n\n// compileParser: pre-compiles a format string into an object whose\n// parse()/safeParse()/parseToParts() methods skip the per-call\n// pattern-cache lookup. The patternCache in this module means a plain\n// parse(fmt, input) call already pays only a Map lookup after the first\n// call, so compileParser is mostly an ergonomics affordance — useful\n// for callers who want to hold the compiled parser explicitly (e.g. to\n// inspect the pattern via the .pattern property).\nexport interface CompiledParser {\n parse(input: string, options?: NumberingParseOptions): unknown;\n safeParse(input: string, options?: NumberingParseOptions): SafeParseResult;\n tryParse(input: string, options?: NumberingParseOptions): unknown | undefined;\n parseToParts(input: string, options?: NumberingParseOptions): ParsedPart[];\n readonly formatStr: string;\n readonly pattern: CapturingPattern;\n}\n\nexport function compileParser(formatStr: string, options: NumberingParseOptions = {}): CompiledParser {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new FormatSyntaxError({\n format: formatStr,\n message:\n `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`,\n });\n }\n // Pre-compile against the default locale; per-call locales will\n // re-resolve via getPattern() if they differ. Most callers use one\n // locale consistently, so pre-compiling against the default keeps\n // the fast path fast.\n const locale = options.locale ?? DEFAULT_LOCALE;\n const pattern = getPattern(formatStr, locale);\n return {\n formatStr,\n pattern,\n parse(input: string, opts: NumberingParseOptions = {}) {\n return parse(formatStr, input, { locale, ...opts });\n },\n safeParse(input: string, opts: NumberingParseOptions = {}) {\n return safeParse(formatStr, input, { locale, ...opts });\n },\n tryParse(input: string, opts: NumberingParseOptions = {}) {\n return tryParse(formatStr, input, { locale, ...opts });\n },\n parseToParts(input: string, opts: NumberingParseOptions = {}) {\n return parseToParts(formatStr, input, { locale, ...opts });\n },\n };\n}","import { type FormatOptions } from './tokens.js';\nimport { tokenize } from './tokenize.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { canonicalCacheKey, normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\n// A Temporal.Duration doesn't sit on a calendar — it has no year/month/day\n// position the way a PlainDate does — so the date/time token set in\n// tokens.ts doesn't apply. This is a separate token table for the same\n// reason parseRelative() lives in its own module: a different surface\n// deserves its own grammar, not a bolt-on to the existing one.\n//\n// Token design: each unit has three forms, in increasing verbosity.\n// `y` — numeric value, no unit text (\"2\")\n// `yy` — short unit suffix, plural-aware (\"2y\", or \"1y\" — see below)\n// `yyy` — full unit word, plural-aware (\"2 years\", \"1 year\")\n// Same for months (`o`/`oo`/`ooo` — `o` not `M` because the duration's\n// own field name in Temporal is \"months\" and the long-form output should\n// read \"month\"/\"months\", but the *token* letter is arbitrary; `o` is\n// chosen so it doesn't visually collide with `m` (minutes) or `M`\n// (months in date format) when a reader skims both tables side by side).\n//\n// Locale-aware rendering: when a `locale` is passed, the short/long\n// forms delegate to `Intl.NumberFormat` with `style: 'unit'`. That gives\n// us pluralization, native unit names, and the locale's preferred\n// spacing for free, matching the same \"delegate to Intl\" approach\n// `formatDistance` already uses. Without a `locale`, the original\n// English-only hardcoded table is used so existing default output stays\n// byte-identical — this is an additive locale option, not a rewrite of\n// the default rendering path.\n\ntype UnitKey = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface UnitForms {\n // singular and plural suffixes for the long form (e.g. \"year\", \"years\")\n longSingular: string;\n longPlural: string;\n // singular and plural suffixes for the short form (e.g. \"yr\", \"yrs\")\n shortSingular: string;\n shortPlural: string;\n // the property on Temporal.Duration to read for this unit\n field: UnitKey;\n // Intl.NumberFormat unit identifier (used only on the locale-aware path).\n // Confirmed against the current Intl spec — including `millisecond`, which\n // is in fact supported by every engine we target (Node 20+, modern ICU).\n // The task brief flagged this as a possible gap; empirically it isn't.\n intlUnit: string;\n}\n\nconst DURATION_UNITS: Record<string, UnitForms> = {\n y: { longSingular: 'year', longPlural: 'years', shortSingular: 'yr', shortPlural: 'yrs', field: 'years', intlUnit: 'year' },\n o: { longSingular: 'month', longPlural: 'months', shortSingular: 'mo', shortPlural: 'mos', field: 'months', intlUnit: 'month' },\n w: { longSingular: 'week', longPlural: 'weeks', shortSingular: 'wk', shortPlural: 'wks', field: 'weeks', intlUnit: 'week' },\n d: { longSingular: 'day', longPlural: 'days', shortSingular: 'd', shortPlural: 'd', field: 'days', intlUnit: 'day' },\n h: { longSingular: 'hour', longPlural: 'hours', shortSingular: 'h', shortPlural: 'h', field: 'hours', intlUnit: 'hour' },\n m: { longSingular: 'minute', longPlural: 'minutes', shortSingular: 'm', shortPlural: 'm', field: 'minutes', intlUnit: 'minute' },\n s: { longSingular: 'second', longPlural: 'seconds', shortSingular: 's', shortPlural: 's', field: 'seconds', intlUnit: 'second' },\n S: { longSingular: 'millisecond', longPlural: 'milliseconds', shortSingular: 'ms', shortPlural: 'ms', field: 'milliseconds', intlUnit: 'millisecond' },\n};\n\n// Token strings this module recognizes, longest-first for the greedy\n// tokenizer. tokenize() in tokenize.ts already does longest-first via\n// SORTED_TOKEN_STRINGS, but it operates on the *date/time* TOKENS table —\n// not this one. Rather than teach tokenize() about two tables, this\n// module uses its own equivalent scan over DURATION_UNITS keys.\nconst DURATION_TOKEN_STRINGS = Object.keys(DURATION_UNITS)\n .flatMap((single) => [single + single + single, single + single, single])\n .sort((a, b) => b.length - a.length);\n\ntype Piece =\n | { kind: 'token'; value: string; unit: string; form: 'numeric' | 'short' | 'long' }\n | { kind: 'literal'; value: string };\n\n// Same greedy-match shape as tokenize() in tokenize.ts, but with this\n// module's own token set. Quoted-literal handling is identical so the\n// same documented escaping rules carry over: 'at' is a literal, '' is a\n// literal quote.\nfunction tokenizeDuration(format: string): Piece[] {\n const pieces: Piece[] = [];\n let i = 0;\n while (i < format.length) {\n const ch = format[i];\n if (ch === \"'\") {\n if (format[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\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 if (!closed) {\n throw new Error(`temporal-fmt: unterminated quote in duration format string \"${format}\"`);\n }\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = DURATION_TOKEN_STRINGS.find((tok) => format.startsWith(tok, i));\n if (match) {\n // single letter = numeric, double = short, triple = long\n const unit = match[0]!;\n const form = match.length === 1 ? 'numeric' : match.length === 2 ? 'short' : 'long';\n pieces.push({ kind: 'token', value: match, unit, form });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n}\n\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\n// Reads a unit value off the duration in a way that works for both real\n// Temporal.Duration objects and field bags ({ hours: 2, minutes: 30 }).\n// Temporal.Duration values are numbers, possibly negative for\n// backwards durations; a field bag the caller passes might leave fields\n// off entirely (undefined → treated as 0 for this unit).\nfunction readUnit(duration: Record<string, unknown>, field: UnitKey): number {\n const value = duration[field];\n if (value === undefined || value === null) return 0;\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n // Temporal.Duration values are always numbers, but a hand-built field\n // bag could end up with a string here. Coerce; throw on garbage rather\n // than silently formatting NaN.\n const coerced = Number(value);\n if (!Number.isFinite(coerced)) {\n throw new Error(\n `temporal-fmt: duration field \"${field}\" is not a finite number (got ${String(value)}).`\n );\n }\n return coerced;\n}\n\n// Intl.NumberFormat is expensive to construct, and a typical duration\n// render walks 2-4 units — so a tight loop on the same (locale, unit,\n// display) triple would otherwise rebuild the formatter for every token\n// of every row. Cache by canonical-locale + unit + display, same\n// eviction shape as the other locale-keyed caches in this library\n// (formatterCache in tokens.ts, rtfCache in formatDistance.ts).\nconst unitFormatterCache = new Map<string, Intl.NumberFormat>();\nconst MAX_UNIT_FORMATTER_CACHE_SIZE = 200;\n\nfunction getUnitFormatter(locale: string, intlUnit: string, unitDisplay: 'short' | 'long'): Intl.NumberFormat {\n const key = `${canonicalCacheKey(locale)}|${intlUnit}|${unitDisplay}`;\n const cached = unitFormatterCache.get(key);\n if (cached) return cached;\n if (unitFormatterCache.size >= MAX_UNIT_FORMATTER_CACHE_SIZE) {\n const oldestKey = unitFormatterCache.keys().next().value;\n if (oldestKey !== undefined) unitFormatterCache.delete(oldestKey);\n }\n let formatter: Intl.NumberFormat;\n try {\n formatter = new Intl.NumberFormat(normalizeLocaleTag(locale), {\n style: 'unit',\n unit: intlUnit,\n unitDisplay,\n });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n unitFormatterCache.set(key, formatter);\n return formatter;\n}\n\nexport interface DurationFormatOptions extends FormatOptions {\n /**\n * When true, units whose value is zero are still emitted in the output\n * (e.g. \"0 hours, 30 minutes\" instead of \"30 minutes\"). Default is\n * false: zero-value units are omitted, matching how date-fns's\n * formatDuration works and how most callers rendering a duration to\n * a human would want it.\n */\n showZeroValues?: boolean;\n}\n\n/**\n * Format a Temporal.Duration (or a plain field bag { years, months, ...\n * }) using a duration-specific token string. Token grammar is documented\n * in the README under \"Duration formatting\" — it does NOT reuse the\n * date/time token table, since a duration has no calendar position.\n *\n * Zero-value units are omitted by default; pass { showZeroValues: true }\n * to force them to appear.\n *\n * Unit-name localization: without a `locale`, output is the original\n * English hardcoded singular/plural forms (byte-identical to previous\n * versions). With a `locale`, the short/long forms delegate to\n * `Intl.NumberFormat`'s `style: 'unit'` — same approach `formatDistance`\n * already uses for `Intl.RelativeTimeFormat`. Numeric-only tokens\n * (`y`, `o`, `w`, ...) are not affected by `locale`; they remain ASCII\n * digits, matching the rest of this library's \"numbers stay Western\"\n * convention.\n *\n * @example\n * formatDuration(Temporal.Duration.from({ years: 2, months: 1 }), 'yyy ooo')\n * // \"2 years 1 month\"\n * formatDuration({ hours: 2, minutes: 30 }, 'hhh mmm', { locale: 'fr-FR' })\n * // \"2 heures 30 minutes\"\n */\nexport function formatDuration(\n duration: Record<string, unknown>,\n formatStr: string,\n options: DurationFormatOptions = {},\n): string {\n return renderDurationPieces(duration, formatStr, options).map((p) => p.value).join('');\n}\n\n// One rendered piece of a duration format string: either literal text or\n// a token with its already-rendered value (empty string for a\n// zero-suppressed token, matching formatDurationToParts' documented\n// contract of emitting an empty token part rather than dropping it).\nexport interface RenderedDurationPiece {\n kind: 'literal' | 'token';\n value: string;\n token?: string;\n}\n\n// Shared renderer behind both formatDuration() (joins the pieces) and\n// formatDurationToParts() (returns them). Single source of truth, so the\n// joined string and the parts can never disagree — the old\n// formatDurationToParts re-derived part boundaries by searching the\n// rendered string with indexOf and mis-sliced whenever a literal's text\n// also occurred inside an earlier token's rendered value.\nexport function renderDurationPieces(\n duration: Record<string, unknown>,\n formatStr: string,\n options: DurationFormatOptions = {},\n): RenderedDurationPiece[] {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(\n `temporal-fmt: duration format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters ` +\n `(got ${formatStr.length}).`\n );\n }\n\n const showZeroes = options.showZeroValues === true;\n // Undefined locale → keep the original hardcoded English path so\n // existing default output is byte-identical. Any locale string passed\n // (including 'en-US') takes the Intl.NumberFormat path — so an\n // explicit `locale: 'en-US'` will produce Intl's spacing/word choices\n // rather than the hand-rolled English table. That's intentional: the\n // locale-aware path delegates to Intl, the default path doesn't.\n const locale = options.locale;\n const useIntl = locale !== undefined;\n\n const pieces = tokenizeDuration(formatStr);\n const out: RenderedDurationPiece[] = [];\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n out.push({ kind: 'literal', value: piece.value });\n continue;\n }\n\n const unit = DURATION_UNITS[piece.unit];\n // Dead by construction: piece.unit always comes from match[0] where\n // match is a member of DURATION_TOKEN_STRINGS, itself built\n // exhaustively from DURATION_UNITS' own keys — so this lookup can\n // never miss. Kept in case tokenizeDuration's token construction\n // ever diverges from DURATION_UNITS.\n /* c8 ignore start */\n if (!unit) {\n throw new Error(`temporal-fmt: unknown duration token \"${piece.value}\"`);\n }\n /* c8 ignore stop */\n\n const value = readUnit(duration, unit.field);\n if (value === 0 && !showZeroes) {\n // Omit zero-value units entirely. The token's contribution to the\n // output is empty — but this can leave a dangling separator literal\n // (e.g. \"2 hours, \" with nothing after). Callers who want clean\n // output should structure their format string to not put a\n // separator after a unit that might be zero, or use showZeroValues.\n out.push({ kind: 'token', value: '', token: piece.value });\n continue;\n }\n\n if (piece.form === 'numeric') {\n out.push({ kind: 'token', value: String(value), token: piece.value });\n continue;\n }\n\n if (useIntl) {\n // Intl.NumberFormat handles pluralization, native unit names, and\n // locale-appropriate spacing all in one call — no point replicating\n // that here. We pass the value as-is so negative durations\n // (`-1 hour`) round-trip the sign the way Intl expects.\n const unitDisplay = piece.form === 'short' ? 'short' : 'long';\n const formatter = getUnitFormatter(locale!, unit.intlUnit, unitDisplay);\n out.push({ kind: 'token', value: formatter.format(value), token: piece.value });\n continue;\n }\n\n // Default-path English. Plural rule based on absolute value matches\n // what Intl does for unit style on en-US — only |value| === 1 is\n // singular, including for negatives like \"-1 hour\" (which is what\n // Intl produces for en-US too).\n if (piece.form === 'short') {\n out.push({ kind: 'token', value: value + (value === 1 || value === -1 ? unit.shortSingular : unit.shortPlural), token: piece.value });\n } else {\n out.push({ kind: 'token', value: value + ' ' + (value === 1 || value === -1 ? unit.longSingular : unit.longPlural), token: piece.value });\n }\n }\n\n return out;\n}","import { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { dayOfYear, isGregorianLeapYear } from './isoWeek.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\n// Reads date fields off a Temporal value in the same minimal-shape style\n// as the rest of the library (TemporalLike) — no Temporal factory needed.\n// Works for PlainDate, PlainDateTime, and ZonedDateTime (since they all\n// expose year/month/day). PlainTime has no date fields; formatDistance()\n// between two PlainTime values isn't really meaningful and isn't\n// supported — it'll throw below when the field read fails.\ninterface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction readFields(value: unknown, label: string): DateFieldView {\n if (value === null || typeof value !== 'object') {\n throw new Error(\n `temporal-fmt: formatDistance expects Temporal values, got ${label} = ${String(value)}.`\n );\n }\n const obj = value as Record<string, unknown>;\n // Either year/month/day (a real date) or none of them (a PlainTime);\n // anything in between would be a malformed Temporal-like.\n const hasYear = typeof obj.year === 'number';\n const hasMonth = typeof obj.month === 'number';\n const hasDay = typeof obj.day === 'number';\n if (hasYear !== hasMonth || hasMonth !== hasDay) {\n throw new Error(\n `temporal-fmt: formatDistance got a ${label} with a partial date (some of year/month/day missing). ` +\n `Pass a full Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return {\n year: hasYear ? (obj.year as number) : undefined,\n month: hasMonth ? (obj.month as number) : undefined,\n day: hasDay ? (obj.day as number) : undefined,\n hour: typeof obj.hour === 'number' ? (obj.hour as number) : undefined,\n minute: typeof obj.minute === 'number' ? (obj.minute as number) : undefined,\n second: typeof obj.second === 'number' ? (obj.second as number) : undefined,\n millisecond: typeof obj.millisecond === 'number' ? (obj.millisecond as number) : undefined,\n };\n}\n\n// Reference epoch for ms-since computation. Jan 1, 2000 UTC is the same\n// anchor isoWeek.ts uses for day-of-week arithmetic; reusing it keeps the\n// reasoning localized to one well-known reference date.\nconst REFERENCE_YEAR = 2000;\nconst MS_PER_SECOND = 1000;\nconst MS_PER_MINUTE = 60 * MS_PER_SECOND;\nconst MS_PER_HOUR = 60 * MS_PER_MINUTE;\nconst MS_PER_DAY = 24 * MS_PER_HOUR;\n\nfunction daysSinceReference(year: number, month: number, day: number): number {\n let days = 0;\n if (year >= REFERENCE_YEAR) {\n for (let y = REFERENCE_YEAR; y < year; y++) {\n days += isGregorianLeapYear(y) ? 366 : 365;\n }\n } else {\n for (let y = year; y < REFERENCE_YEAR; y++) {\n days -= isGregorianLeapYear(y) ? 366 : 365;\n }\n }\n days += dayOfYear(year, month, day) - 1;\n return days;\n}\n\nfunction toEpochMs(fields: DateFieldView): number {\n // A PlainDate has no time fields — treat as midnight. A PlainDateTime\n // has them all. Comparing two PlainDates is a day-resolution diff;\n // comparing two PlainDateTimes is millisecond-resolution. Mixing the\n // two (one PlainDate, one PlainDateTime) is allowed and just means the\n // PlainDate side is treated as midnight, which is what most callers\n // would intuitively expect (\"distance from this date to this moment\").\n if (fields.year === undefined || fields.month === undefined || fields.day === undefined) {\n throw new Error(\n `temporal-fmt: formatDistance needs a Temporal value with year/month/day fields ` +\n `(PlainDate, PlainDateTime, or ZonedDateTime). A PlainTime or other shape ` +\n `has no anchor date to diff against.`\n );\n }\n const days = daysSinceReference(fields.year, fields.month, fields.day);\n const hour = fields.hour ?? 0;\n const minute = fields.minute ?? 0;\n const second = fields.second ?? 0;\n const millisecond = fields.millisecond ?? 0;\n return days * MS_PER_DAY + hour * MS_PER_HOUR + minute * MS_PER_MINUTE + second * MS_PER_SECOND + millisecond;\n}\n\n// Unit selection cutoffs. Mirrors the rough thresholds date-fns uses,\n// trimmed to the units Intl.RelativeTimeFormat supports in every engine\n// (it accepts 'second' through 'year'; 'quarter' and 'week' support\n// varies, so we skip 'week' here — going days → months directly at the\n// 30-day boundary is simpler and avoids the engine-support cliff).\n//\n// |diff| < 60 s → seconds (\"30 seconds ago\")\n// |diff| < 60 min → minutes (\"12 minutes ago\")\n// |diff| < 24 h → hours (\"3 hours ago\")\n// |diff| < 30 d → days (\"5 days ago\")\n// |diff| < 365 d → months (\"2 months ago\")\n// otherwise → years (\"1 year ago\")\n//\n// 30 days is an approximation of a month — a calendar month is 28-31\n// days, so the boundary is inherently fuzzy. Same with 365 days for a\n// year. Documented as approximations, not exact calendar arithmetic.\n//\n// `cutoffs` on FormatDistanceOptions overrides individual boundaries\n// per call. DEFAULT_CUTOFFS holds the same numbers as the old hardcoded\n// UNIT_CUTOFFS so callers passing nothing see the same output as\n// before — verified byte-for-byte against the pre-change baseline.\nconst DEFAULT_CUTOFFS = {\n seconds: 60, // 60 s\n minutes: 60, // 60 min\n hours: 24, // 24 h\n days: 30, // 30 d\n months: 365, // 365 d (in days, since the months→years boundary is day-resolution)\n} as const;\n\ntype CutoffKey = keyof typeof DEFAULT_CUTOFFS;\n\n// Cutoffs the caller can override. Each value is in the unit's own\n// native scale (seconds for `seconds`, minutes for `minutes`, etc.) —\n// `months` is the exception: it's the day count that bounds the\n// months→years crossover, since \"months\" itself isn't a fixed number\n// of days. This mirrors how the original hardcoded table expressed\n// the same boundary (30 * MS_PER_DAY for days, 365 * MS_PER_DAY for\n// the months cap) and lets callers say \"treat anything under 90 days\n// as months\" rather than \"anything under 3 months as months\" (which\n// would require picking a definition of \"month\").\nexport interface DistanceCutoffs {\n seconds?: number;\n minutes?: number;\n hours?: number;\n days?: number;\n months?: number;\n}\n\n// Convert the user-facing cutoffs (per-unit numbers) into the ms\n// thresholds the matching loop needs. Validates each value is a\n// positive finite number, and that the boundaries are monotonically\n// non-decreasing in equivalent ms — without that, the smallest-bound\n// branch always wins and a later branch becomes unreachable, which\n// is never what the caller intended.\nfunction resolveCutoffs(user?: DistanceCutoffs): Array<{ maxMs: number; unit: Intl.RelativeTimeFormatUnit }> {\n const merged: Record<CutoffKey, number> = { ...DEFAULT_CUTOFFS, ...user };\n\n for (const key of Object.keys(merged) as CutoffKey[]) {\n const value = merged[key];\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new Error(\n `temporal-fmt: formatDistance cutoff \"${key}\" must be a positive finite number (got ${String(value)}).`\n );\n }\n }\n\n const secondsMs = merged.seconds * MS_PER_SECOND;\n const minutesMs = merged.minutes * MS_PER_MINUTE;\n const hoursMs = merged.hours * MS_PER_HOUR;\n const daysMs = merged.days * MS_PER_DAY;\n // `months` is in days — see DistanceCutoffs comment above.\n const monthsMs = merged.months * MS_PER_DAY;\n\n if (!(secondsMs <= minutesMs && minutesMs <= hoursMs && hoursMs <= daysMs && daysMs <= monthsMs)) {\n throw new Error(\n `temporal-fmt: formatDistance cutoffs must be monotonically non-decreasing in equivalent ms ` +\n `(seconds ≤ minutes ≤ hours ≤ days ≤ months). Got: seconds=${merged.seconds}s, ` +\n `minutes=${merged.minutes}min, hours=${merged.hours}h, days=${merged.days}d, months=${merged.months}d — ` +\n `pick a sequence where each boundary is at least as large as the one before it.`\n );\n }\n\n return [\n { maxMs: secondsMs, unit: 'second' },\n { maxMs: minutesMs, unit: 'minute' },\n { maxMs: hoursMs, unit: 'hour' },\n { maxMs: daysMs, unit: 'day' },\n { maxMs: monthsMs, unit: 'month' },\n ];\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n const key = `${canonicalLocaleKey(locale)}|${numeric}`;\n let rtf = rtfCache.get(key);\n if (rtf) return rtf;\n if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n const oldestKey = rtfCache.keys().next().value;\n if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n }\n try {\n rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n rtfCache.set(key, rtf);\n return rtf;\n}\n\nfunction canonicalLocaleKey(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n return locale;\n }\n}\n\nexport interface FormatDistanceOptions extends FormatOptions {\n /**\n * 'auto' (default) lets Intl.RelativeTimeFormat use natural forms like\n * \"yesterday\"/\"tomorrow\"/\"now\" when the rounded value lands on ±1 or 0.\n * 'always' forces the strict \"1 day ago\"/\"in 1 day\"/\"in 0 seconds\" form.\n */\n numeric?: 'always' | 'auto';\n /**\n * Override the unit-selection boundaries (seconds→minutes,\n * minutes→hours, hours→days, days→months, months→years). Any\n * subset can be supplied; omitted boundaries fall back to the\n * defaults (60s, 60min, 24h, 30d, 365d). Values are in each\n * unit's native scale except `months`, which is in days — see\n * DistanceCutoffs. Throws descriptively on non-monotonic\n * boundaries or non-positive values, rather than producing\n * confusing output downstream.\n */\n cutoffs?: DistanceCutoffs;\n}\n\n/**\n * Returns a human-readable relative-time string describing `date1`\n * relative to `date2`, e.g. \"3 days ago\", \"in 2 hours\", \"now\". Delegates\n * unit names and pluralization to `Intl.RelativeTimeFormat` so the\n * output localizes the same way the rest of the library's locale-aware\n * tokens do.\n *\n * Convention: the result describes `date1`'s position relative to\n * `date2`. `formatDistance(now, threeDaysAgo)` → `\"3 days ago\"` (the\n * past date is described relative to now). `formatDistance(now, twoHoursFromNow)`\n * → `\"in 2 hours\"`. This matches the natural-language reading \"describe\n * the first date as if standing at the second one.\"\n *\n * Unit-selection cutoffs (seconds → minutes → hours → days → months →\n * years) and the rationale for each are documented in the README under\n * \"formatDistance\".\n *\n * @example\n * formatDistance(threeDaysAgo, today) // \"3 days ago\"\n * formatDistance(twoHoursFromNow, today) // \"in 2 hours\"\n * formatDistance(today, today) // \"now\"\n * formatDistance(futureDate, today, {locale:'fr-FR'}) // \"dans 2 jours\"\n */\nexport function formatDistance(\n date1: unknown,\n date2: unknown,\n options: FormatDistanceOptions = {},\n): string {\n const fields1 = readFields(date1, 'date1');\n const fields2 = readFields(date2, 'date2');\n\n // diff > 0: date1 is later than date2 → \"in X units\" (date1 is in\n // the future relative to date2). diff < 0: date1 is earlier than\n // date2 → \"X units ago\". This convention puts date1 in the\n // \"described\" role and date2 in the \"reference\" role, matching\n // natural-language reading: `formatDistance(threeDaysAgo, today)`\n // = \"describe threeDaysAgo as if standing at today\" = \"3 days ago\".\n const diffMs = toEpochMs(fields1) - toEpochMs(fields2);\n const absMs = Math.abs(diffMs);\n\n const unitCutoffs = resolveCutoffs(options.cutoffs);\n let unit: Intl.RelativeTimeFormatUnit = 'year';\n for (const { maxMs, unit: candidateUnit } of unitCutoffs) {\n if (absMs < maxMs) {\n unit = candidateUnit;\n break;\n }\n }\n\n const divisor = unitToMs(unit);\n const rounded = Math.round(diffMs / divisor);\n\n const numeric = options.numeric ?? 'auto';\n const locale = options.locale ?? DEFAULT_LOCALE;\n const rtf = getRtf(locale, numeric);\n return rtf.format(rounded, unit);\n}\n\n/**\n * Same as formatDistance(date, now), where \"now\" is read from the system\n * clock at call time. Convenience wrapper for the common \"how long ago\n * was this\" case, so the caller doesn't have to construct a reference\n * value themselves.\n *\n * Unlike formatRelativeToNow() (in relativeTime.ts), which only needs\n * calendar-day resolution, this reads the full wall-clock time (hour\n * through millisecond) off the system clock — formatDistance's unit\n * selection is ms-resolution, so a date-only \"now\" would misclassify\n * anything under 24 hours old (e.g. \"1 hour ago\" reading as \"in 23\n * hours\" against a midnight-truncated reference). See the test file\n * for a regression case covering exactly that.\n *\n * The reference is captured fresh on every call, not memoized — each\n * call reflects \"now\" at the moment it runs, same expectation\n * formatRelativeToNow() already sets.\n *\n * @example\n * formatDistanceToNow(threeHoursAgo) // \"3 hours ago\"\n * formatDistanceToNow(tomorrow) // \"in 1 day\" (auto: \"tomorrow\")\n */\nexport function formatDistanceToNow(\n date: unknown,\n options: FormatDistanceOptions = {},\n): string {\n const now = new Date();\n const nowFields = {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n hour: now.getHours(),\n minute: now.getMinutes(),\n second: now.getSeconds(),\n millisecond: now.getMilliseconds(),\n };\n return formatDistance(date, nowFields, options);\n}\n\nfunction unitToMs(unit: Intl.RelativeTimeFormatUnit): number {\n switch (unit) {\n case 'second': return MS_PER_SECOND;\n case 'minute': return MS_PER_MINUTE;\n case 'hour': return MS_PER_HOUR;\n case 'day': return MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable: unitToMs is only called\n with `unit` values produced by formatDistance's cutoff loop above,\n which only ever yields 'second'/'minute'/'hour'/'day'/'month'\n (from resolveCutoffs' fixed array) or 'year' (the loop's fallback\n default). 'week' and 'quarter' were never wired into\n resolveCutoffs — formatDistance intentionally skips week-\n resolution output (see the comment above DEFAULT_CUTOFFS) — so\n neither can reach this switch. */\n case 'week': return 7 * MS_PER_DAY;\n /* c8 ignore stop @preserve */\n case 'month': return 30 * MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable, same reason as 'week'\n above: resolveCutoffs never produces 'quarter'. */\n case 'quarter': return 91 * MS_PER_DAY;\n /* c8 ignore stop @preserve */\n case 'year': return 365 * MS_PER_DAY;\n /* c8 ignore start @preserve -- unreachable: every unit\n resolveCutoffs can actually produce ('second' through 'month', or\n 'year' as the loop's fallback default) has an explicit case\n above, so this exhaustive-switch default can't fire at runtime.\n It exists so TS accepts the function as total — the compiler\n can't see through the cutoff-array iteration in formatDistance()\n to know the input is already narrowed. */\n default:\n throw new Error(`temporal-fmt: formatDistance hit unhandled unit \"${String(unit)}\".`);\n /* c8 ignore stop @preserve */\n }\n}","// Relative-grammar registration. Extends the existing\n// parseRelative.ts with an extension point for registering additional\n// grammars beyond the four built in (English, Spanish, French, German).\n//\n// The existing parseRelative() detects the language from the locale\n// option and dispatches to the matching grammar. registerRelativeGrammar()\n// lets a caller add a new language without modifying parseRelative.ts.\n//\n// Grammar shape: a list of matchers, each returning either a Temporal\n// field bag describing the resolved date or null. parseRelative tries\n// each matcher in order until one matches. This mirrors the ENGLISH/\n// SPANISH/FRENCH/GERMAN_GRAMMAR structure parseRelative already uses\n// internally.\n\nimport { getTemporal, type TemporalNamespace } from './temporalProvider.js';\n\nexport interface RelativeGrammarMatch {\n // The Temporal fields the matcher resolved. Used to construct a\n // PlainDate / PlainDateTime via Temporal.PlainDate.from().\n year?: number;\n month?: number;\n day?: number;\n // Optional: instead of computing fields directly, return a function\n // that takes the reference date and returns the resolved date. Used\n // for phrases like \"next Tuesday\" that depend on the reference's\n // weekday.\n resolve?: (reference: { year: number; month: number; day: number; dayOfWeek: number }) => { year: number; month: number; day: number };\n}\n\nexport interface RelativeGrammar {\n // Locale language subtag this grammar handles ('en', 'es', etc.).\n // Matched against the `locale` option's language subtag.\n language: string;\n // Matchers in priority order. First match wins.\n matchers: Array<(input: string) => RelativeGrammarMatch | null>;\n}\n\n// Cap on registered grammars. Replacement registration for an existing\n// language never grows the registry, so the cap only gates new languages —\n// the same convention registerLocaleVocab uses for custom vocabularies.\n// Without it, a caller looping registerRelativeGrammar() grows the array\n// (and the per-language scan in tryRegisteredGrammar) without bound.\nconst MAX_REGISTERED_GRAMMARS = 100;\n\nconst registeredGrammars: RelativeGrammar[] = [];\n\nexport function registerRelativeGrammar(grammar: RelativeGrammar): void {\n if (typeof grammar.language !== 'string' || grammar.language.length === 0) {\n throw new Error('temporal-fmt: registerRelativeGrammar requires a non-empty language string.');\n }\n if (!Array.isArray(grammar.matchers) || grammar.matchers.length === 0) {\n throw new Error('temporal-fmt: registerRelativeGrammar requires at least one matcher.');\n }\n // Replace any existing grammar for the same language.\n const existingIdx = registeredGrammars.findIndex((g) => g.language === grammar.language);\n if (existingIdx >= 0) {\n registeredGrammars[existingIdx] = grammar;\n } else {\n if (registeredGrammars.length >= MAX_REGISTERED_GRAMMARS) {\n throw new RangeError(`temporal-fmt: registerRelativeGrammar reached the ${MAX_REGISTERED_GRAMMARS}-grammar limit.`);\n }\n registeredGrammars.push(grammar);\n }\n}\n\n// Called by parseRelative() to try registered grammars before falling\n// back to the built-in ones. Returns the resolved Temporal.PlainDate or\n// null if no registered grammar matched.\nexport function tryRegisteredGrammar(\n language: string,\n input: string,\n reference: { year: number; month: number; day: number; dayOfWeek: number },\n): unknown | null {\n for (const grammar of registeredGrammars) {\n if (grammar.language !== language) continue;\n for (const matcher of grammar.matchers) {\n const match = matcher(input);\n if (match === null) continue;\n const temporal = getTemporal();\n if (match.resolve) {\n const fields = match.resolve(reference);\n return temporal.PlainDate.from(fields, { overflow: 'reject' });\n }\n return temporal.PlainDate.from(\n { year: match.year ?? reference.year, month: match.month ?? reference.month, day: match.day ?? reference.day },\n { overflow: 'reject' },\n );\n }\n }\n return null;\n}\n\n// Returns the list of registered languages (for introspection).\nexport function listRegisteredGrammars(): string[] {\n return registeredGrammars.map((g) => g.language);\n}\n\n// Suppress unused import — TemporalNamespace is referenced by the type\n// signature above (via the return type of tryRegisteredGrammar, which\n// is the result of temporal.PlainDate.from).\nvoid (undefined as unknown as TemporalNamespace);\n","import { getTemporal } from './temporalProvider.js';\nimport { type TemporalNamespace } from './temporalProvider.js';\nimport { tryRegisteredGrammar } from './relativeGrammar.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\n\n// parseRelative resolves common relative-date phrases (\"next Tuesday\",\n// \"in 3 days\", \"March 5th\") against a reference date, returning a\n// Temporal.PlainDate. It's a different subsystem from the token-based\n// parse(): the input is free natural-language text, not a structurally-\n// typed format string, so this module owns its own grammar rather than\n// bolting a natural-language layer onto the existing tokenizer.\n//\n// Per-language grammars: each locale's grammar lives in its own\n// clearly-labeled section below (ENGLISH_GRAMMAR, SPANISH_GRAMMAR,\n// FRENCH_GRAMMAR, GERMAN_GRAMMAR). The vocabulary and phrase patterns\n// are NOT shared across languages — Spanish \"hace 2 semanas\" and\n// French \"il y a 2 semaines\" are different idioms and get their own\n// regexes. What IS shared is the matching engine (iterate matchers in\n// order, first match wins, throw on no match) and the resolution\n// helpers (addDays, addUnits, weekdayOffset, resolveToNextOccurrence).\n// Same approach the humanizing skill calls \"solve each recurring\n// problem once\": the matching strategy is shared, the per-language\n// vocabulary is not.\n//\n// Default locale (no `locale` option) uses English — matches the\n// pre-change behavior byte-for-byte. Passing `locale: 'es'`,\n// `locale: 'fr'`, or `locale: 'de'` (or any locale tag with that\n// language subtag) routes to the corresponding grammar. Unrecognized\n// languages fall back to English rather than throwing — a caller\n// passing `locale: 'it-IT'` still gets *some* result, just from the\n// English grammar. (We could throw instead; the current contract is\n// \"best effort.\" If the caller wanted Italian specifically, they'd\n// pass an Italian grammar via registerLocaleVocab-style extension —\n// not implemented here.)\n\n// Lowercase + expand German umlauts (ä→ae, ö→oe, ü→ue, ß→ss) + strip\n// remaining diacritics. Three things going on at once:\n//\n// 1. Lowercase — case-insensitive matching without forcing the\n// regex flag to do it (also keeps the captured groups lowercase\n// for the resolver's comparisons).\n// 2. Expand German umlaut transliterations. Germans without an\n// umlaut key write \"ä\" as \"ae\" — the standard transliteration.\n// Expanding \"ä\" → \"ae\" (before NFD stripping) means both \"März\"\n// and \"Maerz\" normalize to \"maerz\" and match the same pattern.\n// The reverse (ae → ä) would be wrong: \"Tage\" has \"ae\" as a\n// coincidence, not as an umlaut transliteration. Forward expansion\n// is unambiguous because the source string already has the\n// umlaut — the expansion only affects the matched form.\n// 3. Strip combining marks via NFD. \"Miércoles\" → \"Miercoles\",\n// \"février\" → \"fevrier\", \"août\" → \"aout\". Pairs with the umlaut\n// expansion above so all four languages' accented forms collapse\n// to one canonical unaccented form for matching.\n//\n// Both sides of the match — input string AND the weekday/month name\n// lists — get the same normalization applied, so this is symmetric.\nfunction normalizeForMatch(s: string): string {\n return s.toLowerCase()\n .replace(/ä/g, 'ae')\n .replace(/ö/g, 'oe')\n .replace(/ü/g, 'ue')\n .replace(/ß/g, 'ss')\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '');\n}\n\n// Standard Levenshtein edit distance (insert/delete/substitute, cost 1\n// each). Used only by the opt-in fuzzy layer below — the exact matcher\n// pass never calls this. Iterative DP over two rows, not the full\n// matrix, since only the previous row is ever needed.\nfunction levenshtein(a: string, b: string): number {\n /* c8 ignore start @preserve -- unreachable through the only call\n * site (fuzzyCorrectEnglish): `a` is always a word that already\n * failed the `vocabulary.includes(word)` exact-match check, so\n * a === b never holds against any `b` drawn from that same\n * vocabulary; and neither `a` (split from a non-empty normalized\n * string) nor `b` (a vocabulary entry) is ever empty. Kept as\n * general-purpose guards in case levenshtein() gets a second caller\n * later. */\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n /* c8 ignore stop @preserve */\n let prev = new Array(b.length + 1);\n let curr = new Array(b.length + 1);\n for (let j = 0; j <= b.length; j++) prev[j] = j;\n for (let i = 1; i <= a.length; i++) {\n curr[0] = i;\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(\n prev[j] + 1, // deletion\n curr[j - 1] + 1, // insertion\n prev[j - 1] + cost, // substitution\n );\n }\n [prev, curr] = [curr, prev];\n }\n return prev[b.length];\n}\n\n// Max edit distance a fuzzy correction is allowed to bridge. 2 covers\n// the common single-finger-slip and single-transposed-letter typo cases\n// (\"tommorow\" is distance 1 from \"tomorrow\"; \"tuesady\" is distance 2\n// from \"tuesday\" — a transposition costs 2 under simple Levenshtein,\n// which doesn't special-case adjacent-swap as a single edit) without\n// opening the door to correcting a word into something only vaguely\n// similar. Deliberately conservative — this is a typo-tolerance layer,\n// not a \"guess what they meant\" layer; the library's whole design\n// philosophy elsewhere (parse()'s strict-by-default ambiguity handling,\n// the lenient opt-in) is to bound how much guessing is allowed and make\n// the caller ask for it explicitly.\nconst FUZZY_MAX_DISTANCE = 2;\nconst MAX_RELATIVE_INPUT_LENGTH = 4096;\nconst MAX_RELATIVE_WORDS = 32;\n\n// English-only fuzzy vocabulary. Anything a matcher's regex treats as a\n// fixed keyword (not a captured number) belongs here — weekday names,\n// month names (long and short), and the marker words the matchers\n// switch on (today/tomorrow/yesterday/next/last/this/in/ago and the\n// unit words day/week/month/year). Numbers and ordinal suffixes are\n// deliberately excluded: \"5th\" vs \"5tj\" isn't a word-shaped typo this\n// layer is trying to solve, and correcting digits is a much easier way\n// to silently produce a wrong date than correcting a weekday name is.\n//\n// Scoped to English only for this pass — the other three grammars have\n// enough positional/multi-word-marker variation (French's \"il y a\",\n// Spanish's dual pre/post weekday-modifier shapes) that a single\n// word-substitution corrector isn't a good fit for all of them without\n// per-language tuning this change doesn't attempt. parseRelative()\n// throws a clear scope-limited error if { fuzzy: true } is combined\n// with a non-English locale, rather than silently no-op-ing.\nfunction buildEnglishFuzzyVocabulary(): string[] {\n return [\n ...ENGLISH_WEEKDAYS,\n ...ENGLISH_MONTHS,\n ...ENGLISH_MONTH_SHORT,\n 'today', 'tomorrow', 'yesterday',\n 'next', 'last', 'this',\n 'in', 'ago',\n 'day', 'days', 'week', 'weeks', 'month', 'months', 'year', 'years',\n ].map(normalizeForMatch);\n}\n\n// Lazily built and cached — the vocabulary is static per process, no\n// need to rebuild the normalized list on every fuzzy call.\nlet englishFuzzyVocabulary: readonly string[] | undefined;\nfunction getEnglishFuzzyVocabulary(): readonly string[] {\n if (!englishFuzzyVocabulary) englishFuzzyVocabulary = Object.freeze(buildEnglishFuzzyVocabulary());\n return englishFuzzyVocabulary;\n}\n\n// Attempts a word-level fuzzy correction of `normalized` against the\n// English vocabulary, returning a corrected string or undefined if no\n// correction was needed/possible. Tokenizes on whitespace; each token\n// that isn't already an exact vocabulary word (or a pure number/ordinal,\n// which passes through untouched) gets replaced with its closest\n// vocabulary match, if one exists within FUZZY_MAX_DISTANCE. If a token\n// has no sufficiently-close vocabulary match, it's left as-is — the\n// re-attempted exact match will then fail on that token same as before,\n// which is the correct outcome for \"this word isn't a typo of anything\n// we know, it's just not a phrase we support.\"\nfunction fuzzyCorrectEnglish(normalized: string): string | undefined {\n const vocabulary = getEnglishFuzzyVocabulary();\n const words = normalized.split(' ');\n if (normalized.length > MAX_RELATIVE_INPUT_LENGTH || words.length > MAX_RELATIVE_WORDS) {\n throw new RangeError(\n `temporal-fmt: parseRelative fuzzy input is too large (maximum ${MAX_RELATIVE_INPUT_LENGTH} characters and ${MAX_RELATIVE_WORDS} words).`,\n );\n }\n let changed = false;\n const corrected = words.map((word) => {\n // Pure digits, or digits with an ordinal suffix (st/nd/rd/th) —\n // never touched by fuzzy correction. See the vocabulary comment\n // above for why.\n if (/^\\d+(?:st|nd|rd|th)?$/.test(word)) return word;\n // Already an exact vocabulary word — nothing to correct.\n if (vocabulary.includes(word)) return word;\n\n let best: string | undefined;\n let bestDist = FUZZY_MAX_DISTANCE + 1;\n for (const candidate of vocabulary) {\n const dist = levenshtein(word, candidate);\n if (dist < bestDist) {\n bestDist = dist;\n best = candidate;\n }\n }\n if (best !== undefined && bestDist <= FUZZY_MAX_DISTANCE) {\n changed = true;\n return best;\n }\n return word;\n });\n\n return changed ? corrected.join(' ') : undefined;\n}\n\ninterface TemporalFieldBag {\n year?: number;\n month?: number;\n day?: number;\n dayOfWeek?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction readYear(reference: TemporalFieldBag): number {\n if (typeof reference.year !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing year.');\n }\n return reference.year;\n}\nfunction readMonth(reference: TemporalFieldBag): number {\n if (typeof reference.month !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing month.');\n }\n return reference.month;\n}\nfunction readDay(reference: TemporalFieldBag): number {\n if (typeof reference.day !== 'number') {\n throw new Error('temporal-fmt: parseRelative reference date is missing day.');\n }\n return reference.day;\n}\n\nfunction extractDayOfWeek(reference: TemporalFieldBag): number {\n const dow = reference.dayOfWeek;\n if (typeof dow !== 'number') {\n throw new Error(\n 'temporal-fmt: parseRelative needs a reference date exposing dayOfWeek (a Temporal.PlainDate / PlainDateTime / ZonedDateTime).'\n );\n }\n return dow;\n}\n\n// Build an alternation regex fragment from a name list, normalized for\n// matching. Longest-first so \"September\" wins over \"Sep\" in the same\n// match position. The caller is responsible for any wrapping groups\n// or anchors — this just produces the inner alternation.\nfunction nameAlternation(names: string[]): string {\n const normalized = names.map(normalizeForMatch);\n // Dedupe — some locales render the long and short form identically\n // (English \"May\" is the same in both MMMM and MMM lists). Keep both\n // in the alternation rather than deduping, since duplicates in a\n // regex alternation don't change behavior and the dedupe would just\n // be a micro-optimization.\n // Sort longest-first so a longer name wins over its own prefix.\n const sorted = [...normalized].sort((a, b) => b.length - a.length);\n // Escape regex metacharacters — names shouldn't contain any, but\n // belt-and-braces avoids a future \"M+1\" month name from breaking\n // the alternation silently.\n return sorted.map((n) => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')).join('|');\n}\n\n// Resolves to a PlainDate by adding `days` to the reference. Used for\n// weekday refs and the today/tomorrow/yesterday family.\nfunction buildPlainDate(temporal: { PlainDate: { from: (fields: Record<string, number>, options?: { overflow?: 'constrain' | 'reject' }) => unknown } }, year: number, month: number, day: number): unknown {\n return temporal.PlainDate.from({ year, month, day }, { overflow: 'reject' });\n}\n\nfunction addDays(\n temporal: { PlainDate: { from: (fields: Record<string, number>) => unknown } },\n reference: TemporalFieldBag,\n days: number,\n): unknown {\n // Build the reference as a real PlainDate so we can use Temporal's\n // own add() for date arithmetic — calendar-correct (handles month\n // boundaries, leap years, etc.) so we don't have to reimplement it.\n const refDate = temporal.PlainDate.from({\n year: readYear(reference),\n month: readMonth(reference),\n day: readDay(reference),\n }) as { add: (duration: Record<string, number>) => unknown };\n return refDate.add({ days });\n}\n\nfunction addUnits(\n temporal: { PlainDate: { from: (fields: Record<string, number>) => unknown } },\n reference: TemporalFieldBag,\n count: number,\n unit: 'day' | 'week' | 'month' | 'year',\n): unknown {\n const refDate = temporal.PlainDate.from({\n year: readYear(reference),\n month: readMonth(reference),\n day: readDay(reference),\n }) as { add: (duration: Record<string, number>) => unknown };\n const duration: Record<string, number> = {};\n if (unit === 'day') duration.days = count;\n if (unit === 'week') duration.weeks = count;\n if (unit === 'month') duration.months = count;\n if (unit === 'year') duration.years = count;\n return refDate.add(duration);\n}\n\n// \"next X\" — strictly future next occurrence. Said on a Tuesday:\n// \"next Tuesday\" = 7 days from now, not today. Rationale: \"this\n// Tuesday\" handles the same-week case, so \"next Tuesday\" staying\n// strictly-future gives the two phrases distinct, non-overlapping\n// meanings. Documented in README \"parseRelative\".\n//\n// \"last X\" — strictly past most recent occurrence. Symmetric to \"next\".\n//\n// \"this X\" — the X of the current ISO week (Mon..Sun). Can land on\n// today, past, or future depending on where in the week today falls.\n//\n// All three resolve the same way across the four supported languages\n// (en/es/fr/de): the equivalent idiom in each language maps to one of\n// these three rel words. The natural-language reading is consistent\n// — \"next Tuesday\" said on a Tuesday means next week's Tuesday in\n// every language we support.\nfunction weekdayOffset(rel: 'next' | 'last' | 'this', refDow: number, targetDow: number): number {\n // refDow and targetDow are both 1..7 (ISO Mon=1..Sun=7).\n if (rel === 'next') {\n let diff = targetDow - refDow;\n if (diff <= 0) diff += 7; // 0 (today) → 7, negative → wrap forward\n return diff; // 1..7\n }\n if (rel === 'last') {\n let diff = targetDow - refDow;\n if (diff >= 0) diff -= 7; // 0 (today) → -7, positive → wrap backward\n return diff; // -7..-1\n }\n // rel === 'this' — X of the current week. Can be today, past, or future.\n return targetDow - refDow; // -6..+6\n}\n\n// \"March 5th\" without a year → resolve to the next occurrence of that\n// month/day. If today is that date, return today. Otherwise, if it's\n// already past this year, return next year's occurrence.\n//\n// \"Next occurrence\" semantics: future-leaning. Documented in README\n// as the chosen behavior. The alternative (\"nearest in time, past or\n// future\") would mean \"March 5th\" said on March 6 returns yesterday —\n// counterintuitive for the typical \"next birthday\"/\"next deadline\"\n// use case this kind of phrase tends to drive.\nfunction resolveToNextOccurrence(\n temporal: {\n PlainDate: {\n from: (fields: Record<string, number>, options?: { overflow?: 'constrain' | 'reject' }) => unknown;\n compare?: (one: unknown, two: unknown) => number;\n };\n },\n reference: TemporalFieldBag,\n month: number,\n day: number,\n): unknown {\n const refYear = readYear(reference);\n const refDate = temporal.PlainDate.from({\n year: refYear,\n month: readMonth(reference),\n day: readDay(reference),\n });\n // PlainDate.compare is a static method on the namespace, not an\n // instance method — that's why it's called off temporal.PlainDate\n // rather than off an instance below. Checked outside the try/catch\n // below on purpose: that block's catch is scoped to Feb-29-style\n // overflow errors from from(), and silently retries with next year's\n // date. A missing-compare error thrown from inside that try would get\n // caught by the same handler and swallowed by that retry (next year's\n // from() usually succeeds), masking the real problem instead of\n // surfacing it.\n if (!temporal.PlainDate.compare) {\n throw new Error('temporal-fmt: parseRelative needs Temporal.PlainDate.compare to resolve month-day phrases; the active implementation does not expose it.');\n }\n // try this year first; if Feb 29 in a non-leap year, Temporal will\n // throw via overflow: 'reject' — fall through to next year.\n try {\n const thisYear = temporal.PlainDate.from({ year: refYear, month, day }, { overflow: 'reject' });\n // Returns -1 / 0 / 1.\n if (temporal.PlainDate.compare(thisYear, refDate) >= 0) {\n // today or in the future → use this year's occurrence\n return thisYear;\n }\n // already past this year → next year's occurrence\n return temporal.PlainDate.from({ year: refYear + 1, month, day }, { overflow: 'reject' });\n } catch (err) {\n // Feb 29 in a non-leap year, or similar. Try next year — if the\n // caller asked for \"Feb 29\" said in a non-leap year, next year may\n // also not be leap, in which case the inner from() will throw and\n // surface a clear error rather than silently landing on Feb 28.\n try {\n return temporal.PlainDate.from({ year: refYear + 1, month, day }, { overflow: 'reject' });\n } catch {\n throw new Error(\n `temporal-fmt: parseRelative can't resolve month ${month} day ${day} — ` +\n `it isn't a valid date in either ${refYear} or ${refYear + 1}. ` +\n `Original error: ${(err as Error).message}`\n );\n }\n }\n}\n\n// Each language provides a list of matchers. The shared engine\n// iterates them in order and returns the first match. A matcher is a\n// (regex, resolver) pair — the regex captures the structural pieces\n// (count, unit name, weekday name, etc.), the resolver turns them\n// into a Temporal.PlainDate using the shared helpers above.\ninterface ResolveContext {\n temporal: TemporalNamespace;\n reference: TemporalFieldBag;\n}\n\ninterface Matcher {\n pattern: RegExp;\n resolve: (match: RegExpMatchArray, ctx: ResolveContext) => unknown;\n}\n\ninterface RelativeDateGrammar {\n // Single-line description of supported phrase classes, included in\n // the \"doesn't recognize\" error so the caller knows what to try.\n supportedHint: string;\n // Ordered matchers; first match wins.\n matchers: Matcher[];\n}\n\nfunction weekdayIndexFromName(name: string, names: string[]): number {\n const normalized = normalizeForMatch(name);\n const idx = names.findIndex((n) => normalizeForMatch(n) === normalized);\n /* c8 ignore start @preserve -- unreachable: every caller passes a\n * name captured by nameAlternation(names), which builds its regex\n * alternation from this same list under the same normalizeForMatch\n * transform. The regex can't match anything findIndex() wouldn't\n * also find here. Defensive throw kept for the type checker and as\n * a tripwire if that invariant ever breaks. Ignore comment sits on\n * the `if` itself, not just the throw inside it — c8 tracks the\n * branch outcome (was the true side ever taken) separately from the\n * statements inside, so excluding only the inner throw still leaves\n * the branch itself flagged as uncovered. */\n /* c8 ignore next */\n if (idx < 0) {\n throw new Error(`temporal-fmt: parseRelative internal error — weekday \"${name}\" not in names list.`);\n }\n /* c8 ignore stop @preserve */\n return idx + 1;\n}\n\nfunction monthIndexFromName(name: string, longNames: string[], shortNames: string[] = []): number {\n const normalized = normalizeForMatch(name);\n const longIdx = longNames.findIndex((n) => normalizeForMatch(n) === normalized);\n if (longIdx >= 0) return longIdx + 1;\n const shortIdx = shortNames.findIndex((n) => normalizeForMatch(n) === normalized);\n /* c8 ignore next */\n if (shortIdx >= 0) return shortIdx + 1;\n /* c8 ignore start @preserve -- unreachable: every caller passes a name\n * captured by nameAlternation([...longNames, ...shortNames]), so the\n * regex alternation and these two findIndex() scans are built from\n * the same lists under the same normalizeForMatch transform. Kept as\n * a tripwire for the type checker in case that invariant ever breaks. */\n throw new Error(`temporal-fmt: parseRelative internal error — month \"${name}\" not in names list.`);\n /* c8 ignore stop @preserve */\n /* c8 ignore next -- defensive block is unreachable by construction. */\n}\n\n// English grammar — also the default when no locale is supplied. The\n// regexes here are the same ones the pre-change parseRelative() used\n// inline; they've just been pulled into a matcher list so the matching\n// engine can iterate them the same way it does for es/fr/de.\nconst ENGLISH_WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];\nconst ENGLISH_MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nconst ENGLISH_MONTH_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\nconst ENGLISH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Supported: weekday refs (\"next Tuesday\"), day offsets (\"today\"/\"tomorrow\"/\"yesterday\"), unit offsets (\"in 3 days\", \"2 weeks ago\"), and month-day (\"March 5th\").',\n matchers: [\n // today / tomorrow / yesterday — exact match, most rigid shape.\n // Checked first because they're the most common phrase class.\n {\n pattern: /^today$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^tomorrow$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^yesterday$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"next/last/this Tuesday\". Capture the relative\n // word and the weekday name separately, then resolve via +7 / -7.\n {\n pattern: new RegExp(`^(next|last|this)\\\\s+(${nameAlternation(ENGLISH_WEEKDAYS)})$`, 'i'),\n resolve: (m, ctx) => {\n const rel = m[1]!.toLowerCase() as 'next' | 'last' | 'this';\n const targetDow = weekdayIndexFromName(m[2]!, ENGLISH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // unit offsets: \"in 3 days\", \"2 weeks ago\", \"in 1 month\", \"1 year ago\".\n // Two shapes share this matcher — leading \"in\" (future) or trailing\n // \"ago\" (past). Both lead with <number> <unit>.\n {\n pattern: /^(in\\s+)?(\\d+)\\s+(day|week|month|year)s?(?:\\s+ago)?$/i,\n resolve: (m, ctx) => {\n const inPrefix = m[1];\n const count = m[2]!;\n const unitName = m[3]!.toLowerCase() as 'day' | 'week' | 'month' | 'year';\n // Disambiguate future vs past: \"in N units\" is always future; \"N\n // units ago\" is always past. A bare \"N units\" without either is\n // a sign error — throw, since neither direction is implied and\n // guessing is exactly what this library refuses to do.\n const hasIn = !!inPrefix;\n const hasAgo = /\\bago\\b/i.test(m[0]!);\n if (!hasIn && !hasAgo) {\n throw new Error(\n `temporal-fmt: parseRelative can't tell whether \"${m[0]}\" is past or future — ` +\n `use \"in ${count} ${unitName}s\" or \"${count} ${unitName}s ago\".`\n );\n }\n const sign = hasIn ? 1 : -1;\n return addUnits(ctx.temporal, ctx.reference, sign * Number(count), unitName);\n },\n },\n // month-day without year, e.g. \"March 5th\", \"March 5\", \"March 5, 2026\"\n // (year is ignored if present). The ordinal suffix is decorative —\n // strip it. The day-of-month is a 1- or 2-digit number.\n {\n pattern: new RegExp(`^(${nameAlternation([...ENGLISH_MONTHS, ...ENGLISH_MONTH_SHORT])})\\\\s+(\\\\d{1,2})(?:st|nd|rd|th)?(?:,?\\\\s*(\\\\d{4}))?$`, 'i'),\n resolve: (m, ctx) => {\n const month = monthIndexFromName(m[1]!, ENGLISH_MONTHS, ENGLISH_MONTH_SHORT);\n const day = Number(m[2]!);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// Spanish grammar. Distinct phrase shapes from English:\n// - \"hace 2 semanas\" (past unit offset) — distinct word \"hace\", not\n// the English \"X units ago\" suffix shape.\n// - \"5 de marzo\" — day-first, with required \"de\" between day and\n// month, opposite of English \"March 5\".\n// - \"el próximo lunes\" / \"lunes pasado\" — weekday ref has the relative\n// word sometimes as a prefix with article, sometimes as a suffix.\n//\n// All four phrase classes the English grammar supports have Spanish\n// equivalents here.\nconst SPANISH_WEEKDAYS = ['lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado', 'domingo'];\nconst SPANISH_MONTHS = ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'];\n// Spanish month abbreviations vary by region (\"set\" vs \"sept\" for\n// September). Sticking with the most common ICU form per locale.\nconst SPANISH_MONTH_SHORT = ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sept', 'oct', 'nov', 'dic'];\n\nconst SPANISH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Frases soportadas: referencias de día de la semana (\"el próximo lunes\", \"el lunes pasado\", \"este martes\"), offsets de día (\"hoy\"/\"mañana\"/\"ayer\"), offsets de unidad (\"en 3 días\", \"hace 2 semanas\"), y mes-día (\"5 de marzo\").',\n matchers: [\n {\n pattern: /^hoy$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^ma[ñn]ana$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^ayer$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references. Spanish has three common shapes, all meaning\n // the same thing per rel word:\n // \"el próximo lunes\" / \"próximo lunes\" → next\n // \"el lunes próximo\" → next (suffix variant)\n // \"el lunes pasado\" / \"lunes pasado\" → last\n // \"este lunes\" → this\n // Capture the rel word from whichever shape matched, then resolve\n // the same way English does.\n {\n pattern: new RegExp(\n `^(?:el\\\\s+)?(?:(pr[oó]ximo|pasado)\\\\s+(${nameAlternation(SPANISH_WEEKDAYS)})|(${nameAlternation(SPANISH_WEEKDAYS)})\\\\s+(pr[oó]ximo|pasado)|este\\\\s+(${nameAlternation(SPANISH_WEEKDAYS)}))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let relWord: string;\n let weekdayName: string;\n if (m[1] && m[2]) {\n // prefix variant: \"próximo lunes\" / \"pasado lunes\"\n relWord = m[1]!;\n weekdayName = m[2]!;\n } else if (m[3] && m[4]) {\n // suffix variant: \"lunes próximo\" / \"lunes pasado\"\n weekdayName = m[3]!;\n relWord = m[4]!;\n } else {\n // \"este lunes\"\n relWord = 'este';\n weekdayName = m[5]!;\n }\n const rel = normalizeForMatch(relWord) === 'proximo' ? 'next' : relWord.toLowerCase() === 'este' ? 'this' : 'last';\n const targetDow = weekdayIndexFromName(weekdayName, SPANISH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // unit offsets. Spanish uses distinct markers:\n // \"en 3 días\" / \"dentro de 3 días\" → future\n // \"hace 2 semanas\" → past\n // Both shapes lead with the marker, unlike English where one\n // shape leads and the other trails.\n {\n pattern: new RegExp(\n `^(?:(en|dentro\\\\s+de)\\\\s+(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?|(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?\\\\s+(hace))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let count: string;\n let unitWord: string;\n let sign: number;\n if (m[1] && m[2] && m[3]) {\n // future: \"en 3 días\" / \"dentro de 3 días\"\n count = m[2]!;\n unitWord = m[3]!;\n sign = 1;\n } else {\n // past: \"2 semanas hace\" — but the more natural Spanish is\n // \"hace 2 semanas\" (marker first). The regex above only\n // matches \"N units hace\"; that's a less common but\n // grammatical alternative. The primary past form \"hace N\n // unidades\" is matched by the next matcher.\n count = m[4]!;\n unitWord = m[5]!;\n sign = -1;\n }\n const unit = unitWordToEnglish(unitWord);\n return addUnits(ctx.temporal, ctx.reference, sign * Number(count), unit);\n },\n },\n // \"hace 2 semanas\" — the primary past-offset shape. Matcher kept\n // separate from the future \"en/dentro de\" matcher above because\n // Spanish word order differs between the two (marker-first for\n // past, marker-first for future too, but with different markers).\n {\n pattern: new RegExp(`^hace\\\\s+(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // bare \"N <unit>\" without \"en\" / \"hace\" — throw, since direction\n // is ambiguous. Matched AFTER the directional shapes so the\n // directional ones win when their marker is present.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(d[ií]a|semana|mes|a[ñn]o)s?$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative no puede decidir si \"${m[0]}\" es pasado o futuro — ` +\n `usa \"en ${m[1]} ${m[2]}s\" o \"hace ${m[1]} ${m[2]}s\".`\n );\n },\n },\n // month-day: \"5 de marzo\" (day first), \"5 de marzo de 2026\" (year\n // ignored if present). Day is 1-2 digits, with optional trailing\n // \".\" (rare in Spanish but tolerated).\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\.?\\\\s+de\\\\s+(${nameAlternation([...SPANISH_MONTHS, ...SPANISH_MONTH_SHORT])})(?:\\\\s+de\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, SPANISH_MONTHS, SPANISH_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// French grammar. Distinct from English/Spanish:\n// - \"il y a 2 semaines\" (past marker is a 3-word phrase, not a single word).\n// - \"mardi prochain\" — relative word is a SUFFIX on the weekday,\n// opposite of English \"next Tuesday\".\n// - \"5 mars\" — day-first, no \"de\" between day and month (unlike\n// Spanish's \"5 de marzo\").\nconst FRENCH_WEEKDAYS = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'];\nconst FRENCH_MONTHS = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'];\nconst FRENCH_MONTH_SHORT = ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'];\n\nconst FRENCH_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Phrases prises en charge : références de jour de la semaine (\"lundi prochain\", \"mardi dernier\", \"ce mercredi\"), décalages de jour (\"aujourd\\'hui\"/\"demain\"/\"hier\"), décalages d\\'unité (\"dans 3 jours\", \"il y a 2 semaines\"), et mois-jour (\"5 mars\").',\n matchers: [\n {\n pattern: /^aujourd'hui$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^demain$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^hier$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"lundi prochain\" (next), \"lundi dernier\"\n // (last), \"ce lundi\" (this). The relative word follows the\n // weekday for next/last, precedes it for this.\n {\n pattern: new RegExp(\n `^(?:(${nameAlternation(FRENCH_WEEKDAYS)})\\\\s+(prochain|dernier)|ce\\\\s+(${nameAlternation(FRENCH_WEEKDAYS)}))$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n let rel: 'next' | 'last' | 'this';\n let weekdayName: string;\n if (m[1] && m[2]) {\n weekdayName = m[1]!;\n rel = m[2]!.toLowerCase() === 'prochain' ? 'next' : 'last';\n } else {\n rel = 'this';\n weekdayName = m[3]!;\n }\n const targetDow = weekdayIndexFromName(weekdayName, FRENCH_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // past: \"il y a 2 semaines\" — multi-word marker.\n {\n pattern: new RegExp(`^il\\\\s+y\\\\s+a\\\\s+(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // future: \"dans 3 jours\"\n {\n pattern: new RegExp(`^dans\\\\s+(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, count, unit);\n },\n },\n // bare \"N <unit>\" without marker — throw.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(jour|semaine|mois|an|ann[eée]e)s?$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative ne peut pas déterminer si \"${m[0]}\" est passé ou futur — ` +\n `utilisez \"dans ${m[1]} ${m[2]}s\" ou \"il y a ${m[1]} ${m[2]}s\".`\n );\n },\n },\n // month-day: \"5 mars\" (day first, no separator word). Year ignored.\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\s+(${nameAlternation([...FRENCH_MONTHS, ...FRENCH_MONTH_SHORT])})(?:\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, FRENCH_MONTHS, FRENCH_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\n// German grammar. Distinct from the others:\n// - Unit words pluralize with -n in the dative after \"in\"/\"vor\":\n// \"in 3 Tagen\" (vs nominative \"3 Tage\"). The regex accepts both\n// since callers may not always get the case right, and accepting\n// both doesn't introduce ambiguity.\n// - \"nächsten Dienstag\" — relative word precedes weekday as a\n// declined adjective, with the weekday capitalized as a noun.\n// - \"5. März\" — day-first with a period after the day number,\n// matching German date convention.\nconst GERMAN_WEEKDAYS = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];\nconst GERMAN_MONTHS = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];\nconst GERMAN_MONTH_SHORT = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];\n\nconst GERMAN_GRAMMAR: RelativeDateGrammar = {\n supportedHint: 'Unterstützte Phrasen: Wochentag-Bezüge (\"nächsten Dienstag\", \"letzten Freitag\", \"diesen Montag\"), Tages-Offsets (\"heute\"/\"morgen\"/\"gestern\"), Einheiten-Offsets (\"in 3 Tagen\", \"vor 2 Wochen\"), und Monat-Tag (\"5. März\").',\n matchers: [\n {\n pattern: /^heute$/i,\n resolve: (_m, ctx) => buildPlainDate(ctx.temporal, readYear(ctx.reference), readMonth(ctx.reference), readDay(ctx.reference)),\n },\n {\n pattern: /^morgen$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, 1),\n },\n {\n pattern: /^gestern$/i,\n resolve: (_m, ctx) => addDays(ctx.temporal, ctx.reference, -1),\n },\n // weekday references: \"nächsten Dienstag\" (next), \"letzten\n // Dienstag\" (last), \"diesen Dienstag\" (this). Adjective precedes\n // the weekday; case varies (nächsten/letzten/diesen all take the\n // weak-declension -en/-en/-en ending when no article, which is\n // convenient — one regex alternation covers all three.\n {\n pattern: new RegExp(`^(n(?:ae|a|ä)chsten|letzten|diesen)\\\\s+(${nameAlternation(GERMAN_WEEKDAYS)})$`, 'i'),\n resolve: (m, ctx) => {\n const adj = normalizeForMatch(m[1]!);\n const rel: 'next' | 'last' | 'this' = (adj === 'nachsten' || adj === 'naechsten') ? 'next' : adj === 'diesen' ? 'this' : 'last';\n const targetDow = weekdayIndexFromName(m[2]!, GERMAN_WEEKDAYS);\n const refDow = extractDayOfWeek(ctx.reference);\n return addDays(ctx.temporal, ctx.reference, weekdayOffset(rel, refDow, targetDow));\n },\n },\n // past: \"vor 2 Wochen\"\n {\n pattern: new RegExp(`^vor\\\\s+(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, -count, unit);\n },\n },\n // future: \"in 3 Tagen\"\n {\n pattern: new RegExp(`^in\\\\s+(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, ctx) => {\n const count = Number(m[1]!);\n const unit = unitWordToEnglish(m[2]!);\n return addUnits(ctx.temporal, ctx.reference, count, unit);\n },\n },\n // bare \"N <unit>\" without \"in\" or \"vor\" — throw.\n {\n pattern: new RegExp(`^(\\\\d+)\\\\s+(Tag(?:e|n|en)?|Woche(?:n)?|Monat(?:e|n|en)?|Jahr(?:e|n|en)?)$`, 'i'),\n resolve: (m, _ctx) => {\n throw new Error(\n `temporal-fmt: parseRelative kann nicht erkennen, ob \"${m[0]}\" Vergangenheit oder Zukunft ist — ` +\n `verwende \"in ${m[1]} ${m[2]}\" oder \"vor ${m[1]} ${m[2]}\".`\n );\n },\n },\n // month-day: \"5. März\" (day-first with period). Year ignored.\n {\n pattern: new RegExp(\n `^(\\\\d{1,2})\\\\.?\\\\s+(${nameAlternation([...GERMAN_MONTHS, ...GERMAN_MONTH_SHORT, 'Maerz', 'Maer'])})(?:\\\\s+(\\\\d{4}))?$`,\n 'i',\n ),\n resolve: (m, ctx) => {\n const day = Number(m[1]!);\n const month = monthIndexFromName(m[2]!, GERMAN_MONTHS, GERMAN_MONTH_SHORT);\n return resolveToNextOccurrence(ctx.temporal, ctx.reference, month, day);\n },\n },\n ],\n};\n\nconst GRAMMARS: Record<string, RelativeDateGrammar> = {\n en: ENGLISH_GRAMMAR,\n es: SPANISH_GRAMMAR,\n fr: FRENCH_GRAMMAR,\n de: GERMAN_GRAMMAR,\n};\n\n// Pick a grammar for a locale. Falls back to English for languages we\n// don't have a grammar for (Italian, Portuguese, etc.) rather than\n// throwing — best-effort, matches how formatDistance treats unknown\n// locales (Intl.RelativeTimeFormat accepts them; here we fall back to\n// English so the call still succeeds with reasonable output).\nfunction grammarForLocale(locale: string | undefined): RelativeDateGrammar {\n if (!locale) return ENGLISH_GRAMMAR;\n try {\n // Intl.Locale requires BCP-47 hyphens — same normalization as the\n // other locale-handling code in this library.\n const tag = new Intl.Locale(locale.replace(/_/g, '-'));\n const lang = tag.language.toLowerCase();\n return GRAMMARS[lang] ?? ENGLISH_GRAMMAR;\n } catch {\n // Malformed locale tag — fall back to English. The actual Intl\n // call downstream will throw if the tag is genuinely unusable,\n // which is a better surface for that error than here.\n return ENGLISH_GRAMMAR;\n }\n}\n\n// Language subtag of a locale option, for dispatching to grammars\n// registered via registerRelativeGrammar(). Mirrors grammarForLocale's\n// normalization and fallback.\nfunction languageOf(locale: string | undefined): string {\n if (!locale) return 'en';\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).language.toLowerCase();\n } catch {\n return 'en';\n }\n}\n\n// Map a per-language unit word back to the English unit name the\n// shared addUnits() helper expects. Each language contributes its own\n// regex alternation of unit words; this function normalizes the\n// captured word back to a unit enum value.\nfunction unitWordToEnglish(word: string): 'day' | 'week' | 'month' | 'year' {\n const w = normalizeForMatch(word);\n // English\n /* c8 ignore next */\n if (w === 'day' || w === 'week' || w === 'month' || w === 'year') return w;\n // Spanish (accent-stripped)\n if (w === 'dia') return 'day';\n if (w === 'semana') return 'week';\n if (w === 'mes') return 'month';\n if (w === 'ano' || w === 'año') return 'year';\n // French\n if (w === 'jour') return 'day';\n if (w === 'semaine') return 'week';\n if (w === 'mois') return 'month';\n if (w === 'an' || w === 'annee') return 'year';\n // German\n if (w === 'tag' || w === 'tagen') return 'day';\n if (w === 'woche' || w === 'wochen') return 'week';\n if (w === 'monat' || w === 'monaten') return 'month';\n /* c8 ignore next */\n if (w === 'jahr' || w === 'jahren') return 'year';\n /* c8 ignore start @preserve -- unreachable: every caller passes a word\n * captured by a regex alternation built from exactly these unit-word\n * forms (day/week/month/year across en/es/fr/de), so the branches\n * above already cover every string that can reach this function.\n * Kept as a tripwire for the type checker in case a grammar adds a\n * unit word here without a matching regex update. */\n throw new Error(`temporal-fmt: parseRelative internal error — unrecognized unit word \"${word}\".`);\n /* c8 ignore stop @preserve */\n /* c8 ignore next -- defensive block is unreachable by construction. */\n}\n\n// Reference: kept exported for the unit tests that need it. The other\n// helpers above are not exported since tests only need to drive\n// parseRelative() through the public API + this one constant for the\n// Tuesday-on-Tuesday fixture.\nexport const WEEKDAY_NAMES = ENGLISH_WEEKDAYS;\nexport const MONTH_NAMES = ENGLISH_MONTHS;\n// Exported for the localeVocab-style stub-test pattern in case a future\n// test wants to verify a grammar's name list against Intl's actual\n// output for that locale — same pattern localeVocab.test.js uses to\n// catch a mismatch between what format() produces and what parse()\n// expects.\nexport const SPANISH_MONTH_NAMES = SPANISH_MONTHS;\nexport const FRENCH_MONTH_NAMES = FRENCH_MONTHS;\nexport const GERMAN_MONTH_NAMES = GERMAN_MONTHS;\nexport const SPANISH_WEEKDAY_NAMES = SPANISH_WEEKDAYS;\nexport const FRENCH_WEEKDAY_NAMES = FRENCH_WEEKDAYS;\nexport const GERMAN_WEEKDAY_NAMES = GERMAN_WEEKDAYS;\nvoid WEEKDAY_NAMES;\nvoid MONTH_NAMES;\n\nexport interface ParseRelativeOptions extends FormatOptions {\n /**\n * Opt-in typo tolerance. When the input doesn't match any phrase\n * exactly, retry after correcting individual words (weekday names,\n * month names, and marker words like \"next\"/\"ago\"/\"tomorrow\") that\n * are within a small edit distance of a known word — e.g.\n * \"tommorow\" → \"tomorrow\", \"next tuesady\" → \"next tuesday\". Numbers\n * are never touched.\n *\n * Off by default, matching this library's throw-rather-than-guess\n * philosophy elsewhere (parse()'s `lenient` option is the same shape\n * of opt-in). English only for this option currently — combining\n * `fuzzy: true` with a non-English `locale` throws rather than\n * silently skipping the fuzzy pass.\n *\n * If no correction produces a recognized phrase, throws the same\n * \"doesn't recognize\" error the exact pass would have thrown.\n */\n fuzzy?: boolean;\n}\n\n/**\n * Resolve common relative-date phrases against a reference date,\n * returning a Temporal.PlainDate. Supports English by default, plus\n * Spanish (es), French (fr), and German (de) via the `locale` option.\n *\n * Supported phrase classes (with examples per language):\n *\n * - weekday refs: \"next Tuesday\" / \"el próximo martes\" / \"mardi prochain\" / \"nächsten Dienstag\"\n * - day offsets: \"today\" / \"hoy\" / \"aujourd'hui\" / \"heute\" (and tomorrow/yesterday equivalents)\n * - unit offsets: \"in 3 days\" / \"en 3 días\" / \"dans 3 jours\" / \"in 3 Tagen\"\n * - month-day without year: \"March 5th\" / \"5 de marzo\" / \"5 mars\" / \"5. März\"\n *\n * Ambiguous-case handling is consistent across all four languages:\n * \"next X\" said on X (today is the named weekday) means 7 days from\n * now, not today. \"last X\" said on X means 7 days ago. See README\n * \"parseRelative\" for the documented behavior of each ambiguous case.\n *\n * Throws a descriptive error for any phrase it doesn't recognize in\n * the target language, rather than guessing. A bare \"5 days\" without\n * \"in\" or \"ago\" (or the per-language equivalent) throws with a message\n * pointing at the disambiguation options.\n *\n * Returns a Temporal.PlainDate for every supported phrase. Time-of-day\n * extensions like \"next Tuesday at 3pm\" aren't supported in this pass.\n */\nexport function parseRelative(\n input: string,\n referenceDate: unknown,\n options: ParseRelativeOptions = {},\n): unknown {\n const reference = referenceDate as TemporalFieldBag;\n const temporal = getTemporal();\n\n // Trim, collapse internal whitespace, strip diacritics, and lowercase.\n // Whitespace normalization is shared with the pre-change behavior;\n // diacritic stripping pairs with nameAlternation()'s normalization so\n // \"mi[eé]rcoles\" and \"miercoles\" both match the same pattern.\n // Lowercasing matches the case-insensitive regex flag explicitly so\n // the user's input never gets case-swapped on its way through the\n // matcher (the regexes are 'i'-flagged, but normalizing here keeps\n // the original-trimmed-string echo in error messages predictable).\n const trimmed = (input ?? '').trim().replace(/\\s+/g, ' ');\n if (trimmed.length === 0) {\n throw new Error('temporal-fmt: parseRelative got an empty input string.');\n }\n if (trimmed.length > MAX_RELATIVE_INPUT_LENGTH) {\n throw new RangeError(\n `temporal-fmt: parseRelative input is too large (maximum ${MAX_RELATIVE_INPUT_LENGTH} characters).`,\n );\n }\n // For error messages, keep the user's original spelling (with accents)\n // so the echo in the \"doesn't recognize\" error matches what they\n // typed. The matching itself uses the diacritic-stripped form so\n // \"mi[eé]rcoles\" / \"miercoles\" / \"MI[ÉE]RCOLES\" all match the same\n // pattern without forcing the user to remember the accent.\n const normalized = normalizeForMatch(trimmed);\n\n const grammar = grammarForLocale(options.locale);\n const ctx: ResolveContext = { temporal, reference };\n\n // Registered grammars (registerRelativeGrammar) take precedence over\n // the built-in ones for their language — that's the documented\n // contract in relativeGrammar.ts (\"try registered grammars before\n // falling back to the built-in ones\"). This wiring was missing\n // entirely before: registered grammars were stored and listed but\n // never consulted, so the extension point silently did nothing.\n // Returns null quickly when nothing is registered for the language.\n const registered = tryRegisteredGrammar(languageOf(options.locale), trimmed, {\n year: reference.year as number,\n month: reference.month as number,\n day: reference.day as number,\n dayOfWeek: reference.dayOfWeek as number,\n });\n if (registered !== null) {\n return registered;\n }\n\n const tryMatch = (text: string): unknown | typeof NO_MATCH => {\n for (const matcher of grammar.matchers) {\n const match = text.match(matcher.pattern);\n if (match) {\n return matcher.resolve(match, ctx);\n }\n }\n return NO_MATCH;\n };\n\n const exactResult = tryMatch(normalized);\n if (exactResult !== NO_MATCH) {\n return exactResult;\n }\n\n if (options.fuzzy) {\n // Fuzzy mode is English-only for this pass — see the vocabulary\n // comment above fuzzyCorrectEnglish for why the other three\n // grammars aren't covered. Fail loudly rather than silently\n // skipping the fuzzy attempt, so a caller relying on fuzzy mode\n // for, say, French input doesn't get a confusing \"doesn't\n // recognize\" error with no indication fuzzy matching never ran.\n if (grammar !== ENGLISH_GRAMMAR) {\n throw new Error(\n `temporal-fmt: parseRelative's { fuzzy: true } option currently only supports English. ` +\n `Pass no locale (or locale: 'en'), or omit { fuzzy: true } for this locale.`\n );\n }\n const corrected = fuzzyCorrectEnglish(normalized);\n if (corrected !== undefined) {\n const fuzzyResult = tryMatch(corrected);\n if (fuzzyResult !== NO_MATCH) {\n return fuzzyResult;\n }\n }\n }\n\n throw new Error(\n `temporal-fmt: parseRelative doesn't recognize \"${trimmed}\". ` +\n grammar.supportedHint\n );\n}\n\n// Sentinel distinguishing \"no matcher matched\" from a legitimate\n// resolve() return value of `undefined`/`null` (matchers can throw\n// their own errors, e.g. the ambiguous-direction case, but none\n// currently return a nullish PlainDate — this sentinel just makes the\n// \"no match\" case unambiguous regardless).\nconst NO_MATCH = Symbol('temporal-fmt: parseRelative no match');\n\n// DEFAULT_LOCALE is imported for the option's default-value documentation\n// above (no locale → English). The symbol itself isn't read at runtime\n// here, but the import keeps the dependency edge explicit so future\n// changes to the default-locale constant propagate here too.\nvoid DEFAULT_LOCALE;","// Type guards for Temporal values. The rest of this library works off the\n// duck-typed `TemporalLike` shape (year/month/day/hour/...) rather than\n// `instanceof Temporal.X`, because `instanceof` breaks across polyfill/\n// native boundaries — a polyfill's `Temporal.PlainDate` is a different\n// constructor from the native one, so a `value instanceof Temporal.PlainDate`\n// check would silently fail when the caller is using a polyfill while the\n// guard happens to be looking at native Temporal, or vice versa. Same\n// reasoning as tokens.ts's `intlSupportsNativeTemporal` probe: identity\n// checks don't survive the polyfill/native swap.\n//\n// These guards check structural shape instead: presence of the type-\n// discriminating method each Temporal type carries. Method presence is\n// part of the Temporal spec contract — every implementation, native or\n// polyfill, must expose the same methods on the same types — so it's a\n// stable signal without dragging in a Temporal namespace.\n//\n// Per-type discriminators (verified against @js-temporal/polyfill's\n// prototype method list, which matches the spec):\n//\n// PlainDate — `toPlainDateTime` + `withCalendar` + year/month/day,\n// no hour. (PlainDateTime also has `toPlainDateTime`'s\n// sibling `toPlainDate`/`toPlainTime`, but carries\n// hour — distinguished by time-field presence.)\n// PlainTime — no `toPlainDateTime`, no `withCalendar`. Has hour,\n// no year/month/day. (The only Temporal date/time type\n// that has neither `withCalendar` nor any `toPlainX`\n// method.)\n// PlainDateTime — `toPlainDate` + `withPlainTime` + year/month/day\n// AND hour. (`withPlainTime` is unique — only\n// PlainDateTime and ZonedDateTime carry it; ZonedDateTime\n// also carries `toInstant`, which PlainDateTime doesn't.)\n// ZonedDateTime — `toInstant` + `withTimeZone`. (Both are unique —\n// only ZonedDateTime carries `withTimeZone`.)\n// Instant — `toZonedDateTimeISO`, no `toInstant`. (Instant IS\n// the instant; the `toInstant` method belongs to\n// ZonedDateTime. `toZonedDateTimeISO` exists on\n// Instant and ZonedDateTime per the spec, but on\n// ZonedDateTime it's not exposed via the prototype\n// in either native or polyfill — only Instant has it\n// as a method. Verified by probing both.)\n// PlainYearMonth — `toPlainDate` (takes a day arg) + year/month,\n// no day. (PlainDate also has `toPlainDate`-shaped\n// methods but carries day; PlainMonthDay also has\n// `toPlainDate` but carries day, not year.)\n// PlainMonthDay — `toPlainDate` + month/day, no year. Plus: lacks\n// `add`/`subtract`/`until`/`since` (calendar arithmetic\n// doesn't apply to a month-day without a year).\n// Duration — `total` + `abs` + `negated`. (All three are unique\n// to Duration; no other Temporal type carries any of them.)\n\n// The shapes below intentionally mirror TemporalLike in tokens.ts but\n// stay loose enough to also match real Temporal objects (which carry\n// the spec methods the field-only TemporalLike doesn't).\ninterface TemporalInstance {\n toInstant?: () => unknown;\n toPlainDate?: (arg?: unknown) => unknown;\n toPlainDateTime?: (arg?: unknown) => unknown;\n toPlainTime?: () => unknown;\n toPlainYearMonth?: () => unknown;\n toPlainMonthDay?: () => unknown;\n toZonedDateTime?: (arg: unknown) => unknown;\n toZonedDateTimeISO?: (arg: unknown) => unknown;\n withCalendar?: (cal: unknown) => unknown;\n withPlainTime?: (t: unknown) => unknown;\n withTimeZone?: (z: unknown) => unknown;\n getTimeZoneTransition?: (opts: unknown) => unknown;\n startOfDay?: () => unknown;\n sign?: () => number;\n blank?: () => boolean;\n total?: (opts: unknown) => number;\n abs?: () => unknown;\n negated?: () => unknown;\n toString?: () => string;\n // Fields from TemporalLike — present on all date-carrying Temporal\n // types but absent on PlainTime (which has no calendar position).\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n calendarId?: string;\n timeZoneId?: string;\n // PlainMonthDay stores its month as `monthCode` (e.g. \"M08\"), not\n // as a numeric `month` field — without a year there's no way to\n // disambiguate which month a calendar-independent numeric month\n // refers to in leap-year-aware calendars, so the spec uses\n // `monthCode` instead. PlainMonthDay is the only Temporal type that\n // exposes `monthCode` without also exposing `month`.\n monthCode?: string;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null;\n}\n\n// Hostile-getter hardening: the structural guards below read properties\n// (year, hour, Symbol.toStringTag, ...) off arbitrary caller values. An\n// object with a throwing getter used to turn isPlainDate(obj) — a function\n// whose whole contract is returning a boolean — into an exception factory.\n// Every exported guard now runs through this wrapper: a throw during\n// probing means \"not one of ours\", not a crash. One shared catch (rather\n// than one per guard) keeps the coverage burden honest too.\nfunction guardOrFalse<T>(impl: (value: unknown) => value is T): (value: unknown) => value is T {\n return (value: unknown): value is T => {\n try {\n return impl(value);\n } catch {\n return false;\n }\n };\n}\n\nfunction hasMethod<T extends keyof TemporalInstance>(\n v: TemporalInstance,\n name: T,\n): boolean {\n return typeof v[name] === 'function';\n}\n\n// Symbol.toStringTag is the spec-mandated brand for Temporal types —\n// every Temporal instance carries a tag like \"Temporal.PlainDate\" via\n// this well-known symbol, regardless of whether it's native or polyfill.\n// Used by the per-type guards as a fast positive signal: a tag match\n// is sufficient, the structural fallback (field presence + method checks)\n// is only consulted when the tag isn't set (which can happen with\n// hand-built mock objects or partial implementations).\nfunction hasTag(value: unknown, typeName: string): boolean {\n if (!isObject(value)) return false;\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag];\n return tag === `Temporal.${typeName}`;\n}\n\nexport const isPlainDate: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainDateImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainDate')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'undefined'\n && hasMethod(v, 'toPlainDateTime')\n && hasMethod(v, 'withCalendar');\n});\n\nexport const isPlainTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // PlainTime is the only Temporal type with `hour` but no `year` and\n // neither `withCalendar` nor any `toPlainX` method. That combination\n // is unique among the eight types.\n return typeof v.hour === 'number'\n && typeof v.year === 'undefined'\n && !hasMethod(v, 'withCalendar')\n && !hasMethod(v, 'toPlainDate')\n && !hasMethod(v, 'toPlainDateTime');\n});\n\nexport const isPlainDateTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainDateTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainDateTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // `withPlainTime` is on PlainDateTime and ZonedDateTime; ZonedDateTime\n // also carries `toInstant`. The combination \"has withPlainTime, no\n // toInstant\" uniquely identifies PlainDateTime.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'number'\n && hasMethod(v, 'withPlainTime')\n && !hasMethod(v, 'toInstant');\n});\n\nexport const isZonedDateTime: (value: unknown) => value is TemporalInstance = guardOrFalse(function isZonedDateTimeImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'ZonedDateTime')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // `withTimeZone` is unique to ZonedDateTime across every Temporal type.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'number'\n && typeof v.hour === 'number'\n && hasMethod(v, 'withTimeZone')\n && hasMethod(v, 'toInstant');\n});\n\nexport const isInstant: (value: unknown) => value is TemporalInstance = guardOrFalse(function isInstantImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'Instant')) return true;\n if (!isObject(value)) return false;\n // Instant carries `toZonedDateTimeISO` and no `toInstant` (it IS the\n // instant). It also has no year/month/day/hour fields — those are\n // only meaningful against a calendar and zone, which Instant alone\n // doesn't carry.\n const v = value as TemporalInstance;\n return hasMethod(v, 'toZonedDateTimeISO')\n && !hasMethod(v, 'toInstant')\n && typeof v.year === 'undefined';\n});\n\nexport const isPlainYearMonth: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainYearMonthImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainYearMonth')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // Has year+month, no day, carries `toPlainDate` (used to attach a day).\n // PlainMonthDay also carries `toPlainDate` but has no `year` field.\n return typeof v.year === 'number'\n && typeof v.month === 'number'\n && typeof v.day === 'undefined'\n && hasMethod(v, 'toPlainDate');\n});\n\nexport const isPlainMonthDay: (value: unknown) => value is TemporalInstance = guardOrFalse(function isPlainMonthDayImpl(value: unknown): value is TemporalInstance {\n if (hasTag(value, 'PlainMonthDay')) return true;\n if (!isObject(value)) return false;\n const v = value as TemporalInstance;\n // PlainMonthDay has `day` and `monthCode` but no `year` and no numeric\n // `month`. The `monthCode` field is the discriminator — every other\n // Temporal type that has a month concept either exposes `month` (numeric,\n // calendar-aware) or doesn't expose months at all (PlainTime, Duration).\n return typeof v.year === 'undefined'\n && typeof v.month === 'undefined'\n && typeof v.day === 'number'\n && typeof v.monthCode === 'string'\n && hasMethod(v, 'toPlainDate');\n});\n\nexport const isDuration: (value: unknown) => value is Record<string, unknown> = guardOrFalse(function isDurationImpl(value: unknown): value is Record<string, unknown> {\n if (hasTag(value, 'Duration')) return true;\n if (!isObject(value)) return false;\n // `total` is unique to Duration across every Temporal type — no other\n // type carries it (verified by probing the prototype of each).\n // `abs` and `negated` are also Duration-only. Checking any one of\n // them is sufficient; checking `total` because it's the most semantically\n // distinctive (a Duration-specific operation no other type would\n // coincidentally need).\n const v = value as TemporalInstance;\n return hasMethod(v, 'total');\n});\n\n// Umbrella guard: any Temporal namespace member. Used by `assertTemporal`\n// below for the \"this needs to be some Temporal thing\" case. Two signals\n// checked in order:\n// 1. `Symbol.toStringTag` — every Temporal type brands itself as\n// `Temporal.X` via this well-known symbol. This is the spec-\n// mandated brand, present on native and polyfill instances alike.\n// 2. The discriminating-method fallback — covers any case where a\n// future/partial Temporal impl doesn't set the tag. Method\n// presence is part of the Temporal spec contract; combined with\n// `equals` + `with` (both present on every Temporal instance),\n// it's a reliable signal that doesn't rely on the toStringTag\n// being set.\nexport const isTemporal: (value: unknown) => value is TemporalInstance = guardOrFalse(function isTemporalImpl(value: unknown): value is TemporalInstance {\n if (!isObject(value)) return false;\n const tag = (value as { [Symbol.toStringTag]?: unknown })[Symbol.toStringTag];\n if (typeof tag === 'string' && tag.startsWith('Temporal.')) return true;\n const v = value as TemporalInstance;\n return hasMethod(v, 'toInstant')\n || hasMethod(v, 'toPlainDate')\n || hasMethod(v, 'toPlainDateTime')\n || hasMethod(v, 'toPlainTime')\n || hasMethod(v, 'toPlainYearMonth')\n || hasMethod(v, 'toPlainMonthDay')\n || hasMethod(v, 'toZonedDateTime')\n || hasMethod(v, 'toZonedDateTimeISO')\n || hasMethod(v, 'withCalendar')\n || hasMethod(v, 'withPlainTime')\n || hasMethod(v, 'withTimeZone')\n || hasMethod(v, 'total');\n});\n\n// Assertion helpers — throw descriptively rather than returning false.\n// Same convention as the rest of the library: descriptive thrown errors\n// instead of silent failures. The thrown Error carries a `valueType`\n// hint in the message so the caller can see what they actually passed.\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object') {\n const ctor = (value as { constructor?: { name?: string } }).constructor;\n if (ctor && typeof ctor.name === 'string' && ctor.name !== 'Object') {\n return `instance of ${ctor.name}`;\n }\n return 'plain object';\n }\n return typeof value;\n}\n\nfunction assertImpl(value: unknown, guard: (v: unknown) => boolean, expectedType: string): void {\n if (!guard(value)) {\n throw new Error(\n `temporal-fmt: expected a Temporal.${expectedType}, got ${describeValue(value)}.`\n );\n }\n}\n\nexport function assertTemporal(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isTemporal, 'object (Instant, PlainDate, PlainTime, PlainDateTime, ZonedDateTime, Duration, PlainYearMonth, or PlainMonthDay)');\n}\nexport function assertInstant(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isInstant, 'Instant');\n}\nexport function assertPlainDate(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainDate, 'PlainDate');\n}\nexport function assertPlainTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainTime, 'PlainTime');\n}\nexport function assertPlainDateTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainDateTime, 'PlainDateTime');\n}\nexport function assertZonedDateTime(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isZonedDateTime, 'ZonedDateTime');\n}\nexport function assertPlainYearMonth(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainYearMonth, 'PlainYearMonth');\n}\nexport function assertPlainMonthDay(value: unknown): asserts value is TemporalInstance {\n assertImpl(value, isPlainMonthDay, 'PlainMonthDay');\n}\nexport function assertDuration(value: unknown): asserts value is Record<string, unknown> {\n assertImpl(value, isDuration, 'Duration');\n}","// Per-token metadata: the structured descriptor this module\n// asks for (meaning, format-capable?, parse-capable?, locale-sensitive?,\n// calendar-sensitive?, timezone-sensitive?, supported Temporal types,\n// round-trip-safe?). One table, kept in sync with TOKENS in tokens.ts\n// by tests (see test/tokenMetadata.test.js — every entry in TOKENS has\n// a metadata entry, and vice versa).\n//\n// This is the single source of truth that analyzeFormat() (section E)\n// and the ESLint plugin / codemod consume — never read the\n// TOKENS array directly for metadata; use TOKEN_METADATA here, or the\n// tokenInfo() / listTokens() wrappers in analyze.ts.\n\nimport type { TemporalLike } from './tokens.js';\n\nexport type TemporalType =\n | 'Instant'\n | 'PlainDate'\n | 'PlainTime'\n | 'PlainDateTime'\n | 'ZonedDateTime'\n | 'PlainYearMonth'\n | 'PlainMonthDay';\n\nexport interface TokenMetadata {\n // Short human-readable description of what the token represents.\n meaning: string;\n // True if format() accepts this token. Every token in TOKENS is\n // format-capable by construction; this flag exists so the table is\n // self-describing for callers (the codemod) that work with token\n // families including format-only tokens from other libraries.\n formatCapable: boolean;\n // True if parse() accepts this token. False for `do`, `ww`, `RRRR`\n // — see FORMAT_ONLY_TOKENS in pattern.ts.\n parseCapable: boolean;\n // True if the output depends on locale (resolves via Intl or\n // custom vocab). MMMM/MMM/EEEE/EEE/a are true; everything else is\n // numeric ASCII and locale-independent.\n localeSensitive: boolean;\n // True if the output depends on the calendar (gregory, hebrew,\n // etc.). Locale-aware tokens inherit calendar sensitivity from\n // the Intl path (a hebrew PlainDate's month name depends on the\n // hebrew calendar). Year/month/day numeric tokens are calendar-\n // sensitive in principle (a Hebrew year/month renders differently\n // than a Gregorian one) but stay ISO-numeric on the format side\n // because tokens.ts reads straight off TemporalLike fields, which\n // are already calendar-specific by the time format() sees them.\n calendarSensitive: boolean;\n // True if the token requires a timeZoneId or offset field — i.e.\n // only ZonedDateTime supports it. zzz and the six offset tokens\n // (X/XX/XXX/x/xx/xxx) are timezone-sensitive.\n timezoneSensitive: boolean;\n // Which Temporal types can supply the field this token needs.\n // PlainTime can't supply year; PlainDate can't supply hour; only\n // ZonedDateTime carries timeZoneId/offset.\n supportedTypes: TemporalType[];\n // True if format(x, fmt) → parse(fmt, output) round-trips back to\n // a value equal to x. False when the format is fundamentally lossy:\n // `do` (ordinal suffix isn't structurally parseable), `ww`/`RRRR`\n // (week + weekday alone can't reconstruct a date without an extra\n // disambiguator), `yy` (two-digit year loses century — strptime\n // heuristic resolves it, but the heuristic isn't symmetric with\n // how `yy` formats BCE years, so round-trip only works for years\n // where 00-68/69-99 lands back on the same century).\n roundTripSafe: boolean;\n}\n\n// Helper: which Temporal types carry each field. Centralized here so\n// the supportedTypes arrays below stay short and the table is easy to\n// audit. Mirrors the field checks in tokens.ts (handler third tuple\n// element).\nconst TYPES_WITH: Record<string, TemporalType[]> = {\n year: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainYearMonth'],\n month: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainYearMonth', 'PlainMonthDay'],\n day: ['PlainDate', 'PlainDateTime', 'ZonedDateTime', 'PlainMonthDay'],\n hour: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n minute: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n second: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n millisecond: ['PlainTime', 'PlainDateTime', 'ZonedDateTime', 'Instant'],\n dayOfWeek: ['PlainDate', 'PlainDateTime', 'ZonedDateTime'],\n timeZoneId: ['ZonedDateTime'],\n offset: ['ZonedDateTime'],\n};\n\n// Every token in TOKENS gets an entry here. Adding a token to TOKENS\n// without adding one here makes test/tokenMetadata.test.js fail.\nexport const TOKEN_METADATA: Record<string, TokenMetadata> = {\n yyyy: {\n meaning: 'Four-digit year (preserves sign for BCE; no truncation).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: true,\n },\n yy: {\n meaning: 'Two-digit year (year % 100). Throws on negative years.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n y: {\n meaning: 'Unpadded year, any width (preserves sign for BCE; no truncation, no minimum digits). Cannot sit directly next to another digit-reading token or a literal starting with a digit — there would be no way to tell where it ends.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: true,\n },\n MMMM: {\n meaning: 'Long month name (locale-aware, e.g. \"January\", \"janvier\").',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n MMM: {\n meaning: 'Short month name (locale-aware, e.g. \"Jan\", \"janv.\").',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n MM: {\n meaning: 'Two-digit month (01-12, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n M: {\n meaning: 'One- or two-digit month (1-12, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n dd: {\n meaning: 'Two-digit day-of-month (01-31, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: true,\n },\n d: {\n meaning: 'One- or two-digit day-of-month (1-31, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: true,\n },\n EEEE: {\n meaning: 'Long weekday name (locale-aware, e.g. \"Monday\", \"lundi\"). Parse cross-checks against the parsed date.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n EEE: {\n meaning: 'Short weekday name (locale-aware, e.g. \"Mon\", \"lun.\"). Parse cross-checks against the parsed date.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n HH: {\n meaning: 'Two-digit hour, 24-hour clock (00-23, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n H: {\n meaning: 'One- or two-digit hour, 24-hour clock (0-23, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n hh: {\n meaning: 'Two-digit hour, 12-hour clock (01-12, zero-padded). Requires `a` token on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n h: {\n meaning: 'One- or two-digit hour, 12-hour clock (1-12, no padding). Requires `a` token on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n mm: {\n meaning: 'Two-digit minute (00-59, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.minute, roundTripSafe: true,\n },\n m: {\n meaning: 'One- or two-digit minute (0-59, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.minute, roundTripSafe: true,\n },\n ss: {\n meaning: 'Two-digit second (00-59, zero-padded).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.second, roundTripSafe: true,\n },\n s: {\n meaning: 'One- or two-digit second (0-59, no padding).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.second, roundTripSafe: true,\n },\n S: {\n meaning: 'Fractional second, 1 digit (tenths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SS: {\n meaning: 'Fractional second, 2 digits (hundredths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSS: {\n meaning: 'Fractional second, 3 digits (milliseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSS: {\n meaning: 'Fractional second, 4 digits (tens of microseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSS: {\n meaning: 'Fractional second, 5 digits (hundred-thousandths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSS: {\n meaning: 'Fractional second, 6 digits (microseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSS: {\n meaning: 'Fractional second, 7 digits (tens of nanoseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSSS: {\n meaning: 'Fractional second, 8 digits (hundred-millionths of a second).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n SSSSSSSSS: {\n meaning: 'Fractional second, 9 digits (nanoseconds).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.millisecond, roundTripSafe: true,\n },\n a: {\n meaning: 'Day period (locale-aware: \"AM\"/\"PM\", \"午前\"/\"午後\", etc.). Required alongside 12-hour tokens on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: true, calendarSensitive: false, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.hour, roundTripSafe: true,\n },\n zzz: {\n meaning: 'IANA time zone id (e.g. \"America/New_York\") or fixed offset (\"+05:30\"). Requires full date+time on parse.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: true,\n },\n X: {\n meaning: 'Numeric UTC offset, short (Z for UTC, ±HH[MM] otherwise). Minutes omitted when zero.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n XX: {\n meaning: 'Numeric UTC offset, no colon (Z for UTC, ±HHMM otherwise).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n XXX: {\n meaning: 'Numeric UTC offset, with colon (Z for UTC, ±HH:MM otherwise).',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n x: {\n meaning: 'Numeric UTC offset, short, never Z (always ±HH[MM]). Lowercase variant of X.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n xx: {\n meaning: 'Numeric UTC offset, no colon, never Z (always ±HHMM). Lowercase variant of XX.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n xxx: {\n meaning: 'Numeric UTC offset, with colon, never Z (always ±HH:MM). Lowercase variant of XXX.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.offset, roundTripSafe: true,\n },\n do: {\n meaning: 'Ordinal day-of-month with English suffix (\"1st\", \"2nd\", \"3rd\", \"4th\", ... 21st, 22nd, ...). Format-only — not parseable.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n Q: {\n meaning: 'Quarter as a digit (1-4). Derived from month.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n QQQ: {\n meaning: 'Quarter with \"Q\" prefix (\"Q1\"..\"Q4\"). Derived from month.',\n formatCapable: true, parseCapable: true, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n ww: {\n meaning: 'ISO 8601 week number (01-53). Format-only — week alone can\\'t reconstruct a date.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: false,\n },\n RRRR: {\n meaning: 'ISO 8601 week-numbering year (4-digit, may differ from calendar year at year boundaries). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: false,\n },\n D: {\n meaning: 'Day of year, unpadded (1-366). Format-only — parsing requires resolving against a year.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n DD: {\n meaning: 'Day of year, 2-digit minimum (01-366). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n DDD: {\n meaning: 'Day of year, 3-digit zero-padded (001-366). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: false, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.day, roundTripSafe: false,\n },\n LLLL: {\n meaning: 'Stand-alone long month name (nominative case in Slavic locales). Identical to MMMM in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n LLL: {\n meaning: 'Stand-alone short month name. Identical to MMM in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.month, roundTripSafe: true,\n },\n cccc: {\n meaning: 'Stand-alone long weekday name. Identical to EEEE in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n ccc: {\n meaning: 'Stand-alone short weekday name. Identical to EEE in most locales.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.dayOfWeek, roundTripSafe: true,\n },\n GGGG: {\n meaning: 'Long era name (locale-aware, e.g. \"Anno Domini\"/\"Before Christ\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n G: {\n meaning: 'Short era name (locale-aware, e.g. \"AD\"/\"BC\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: true, timezoneSensitive: false,\n supportedTypes: TYPES_WITH.year, roundTripSafe: false,\n },\n zzzz: {\n meaning: 'Localized long timezone name (e.g. \"Eastern Standard Time\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: false,\n },\n z: {\n meaning: 'Localized short timezone name (e.g. \"EST\"). Format-only.',\n formatCapable: true, parseCapable: false, localeSensitive: true, calendarSensitive: false, timezoneSensitive: true,\n supportedTypes: TYPES_WITH.timeZoneId, roundTripSafe: false,\n },\n\n};\n\nexport const ALL_TOKEN_NAMES: string[] = Object.keys(TOKEN_METADATA);","// Format-string analysis and introspection. Single source of truth for\n// \"what does this format string need / accept / produce\" — consumed by\n// the ESLint plugin and the codemod\n// so they don't each carry their own token table that drifts out of\n// sync with the runtime. Everything here reads from TOKENS in\n// tokens.ts and TOKEN_METADATA in tokenMetadata.ts, so adding a token\n// to those two tables automatically makes it visible to analyze()\n// without touching this file.\n\nimport { TOKENS, type TemporalLike } from './tokens.js';\nimport { TOKEN_METADATA, type TokenMetadata, type TemporalType } from './tokenMetadata.js';\nimport { tokenize, type Piece } from './tokenize.js';\nimport { _handlerFor } from './format.js';\nimport { UNPADDED_NUMERIC_TOKENS, FORMAT_ONLY_TOKENS } from './pattern.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\n\n// Re-exported so callers can import everything analyzer-related from a\n// single entry point — analyze.ts is the introspection surface.\nexport type { TokenMetadata, TemporalType } from './tokenMetadata.js';\nexport type { Piece } from './tokenize.js';\n\n// tokenizeFormat is the same tokenize() the runtime uses, exposed\n// under a name that signals \"this is the introspection entry point.\"\n// Same implementation, different name — callers reading analyze.ts\n// see a coherent API surface rather than having to know that the\n// runtime tokenizer is the right thing to call.\nexport function tokenizeFormat(formatStr: string): Piece[] {\n return tokenize(formatStr);\n}\n\n// listTokens: every token the runtime recognizes, with metadata. Used\n// by IDE autocomplete data (section AC) and the codemod's \"can this\n// source-library token be translated?\" lookup (section Z).\nexport function listTokens(): Array<{ name: string; metadata: TokenMetadata }> {\n return TOKENS.map(([name]) => ({ name, metadata: TOKEN_METADATA[name]! }));\n}\n\n// tokenInfo: one token's metadata. Returns undefined for unknown\n// tokens rather than throwing — callers (autocomplete, hover docs)\n// want a sentinel, not an exception, when a user types a partial token.\nexport function tokenInfo(name: string): TokenMetadata | undefined {\n return TOKEN_METADATA[name];\n}\n\nexport interface AnalyzedToken {\n name: string;\n // Position in the format string, 0-indexed. Useful for IDE diagnostics\n // that highlight a specific token.\n position: number;\n metadata: TokenMetadata;\n}\n\nexport interface FormatAnalysis {\n // Every token in the format string, in order, with metadata.\n tokens: AnalyzedToken[];\n // Fields (year/month/day/hour/...) any token in this format string\n // reads off the input. Lets a caller statically check \"does this\n // format string need a field PlainDate doesn't have?\"\n requiredFields: string[];\n // Temporal types that can supply every required field. The intersection\n // across all tokens — a format string with both `yyyy` (needs year)\n // and `HH` (needs hour) is compatible only with types carrying both,\n // i.e. PlainDateTime/ZonedDateTime.\n compatibleTypes: TemporalType[];\n // Can parse() accept this format string? False when any token is\n // format-only (do/ww/RRRR per FORMAT_ONLY_TOKENS in pattern.ts).\n parseable: boolean;\n // Does the format string contain any locale-aware token (MMMM/MMM/\n // EEEE/EEE/a)? True means the output depends on the locale option\n // and a registered custom vocab can override the result.\n localeSensitive: boolean;\n // Does the format string contain any calendar-sensitive token?\n // Almost every date/weekday token is, since its output depends on\n // the object's calendar (a hebrew PlainDate's \"MMMM\" is a hebrew\n // month name). Pure-time tokens (HH/mm/ss) are not.\n calendarSensitive: boolean;\n // Does the format string contain zzz or any offset token? If yes,\n // only ZonedDateTime can supply the field.\n timezoneSensitive: boolean;\n // Does the format string contain a run of 2+ adjacent unpadded\n // numeric tokens with no literal between them (Md, dM, Hms, ...)?\n // Such runs can be ambiguous at parse time — strict mode throws,\n // lenient mode picks one split via the documented heuristic.\n ambiguous: boolean;\n // Does format(x, fmt) → parse(fmt, output) round-trip? False when\n // any token is format-only OR when yy is used (century loss).\n roundTripSafe: boolean;\n // Non-fatal issues found during analysis. Each carries a `code`\n // the IDE plugin / CLI can switch on. Empty for clean format strings.\n warnings: Array<{ code: string; message: string }>;\n}\n\nexport function analyzeFormat(formatStr: string): FormatAnalysis {\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 const pieces = tokenize(formatStr);\n const tokens: AnalyzedToken[] = [];\n const requiredFields = new Set<string>();\n const compatibleTypesSet = new Set<TemporalType>();\n let parseable = true;\n let localeSensitive = false;\n let calendarSensitive = false;\n let timezoneSensitive = false;\n let roundTripSafe = true;\n const warnings: Array<{ code: string; message: string }> = [];\n\n // Walk pieces, building up the analysis. Position is computed per\n // token piece (literals count toward offset but don't appear in the\n // tokens array).\n let offset = 0;\n const unpaddedRun: string[] = [];\n const flushRun = () => {\n if (unpaddedRun.length >= 2) {\n warnings.push({\n code: 'AMBIGUOUS_NUMERIC_RUN',\n message: `Adjacent unpadded numeric tokens \"${unpaddedRun.join('')}\" with no separator can be ambiguous to parse. Add a separator or use the padded form (e.g. \"MM\" instead of \"M\").`,\n });\n }\n unpaddedRun.length = 0;\n };\n\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n offset += piece.value.length;\n flushRun();\n continue;\n }\n const metadata = TOKEN_METADATA[piece.value];\n // Dead by construction, confirmed via listTokens(): every token in\n // TOKENS has a matching TOKEN_METADATA entry (51/51), and tokenize()\n // already throws on any string that isn't in TOKENS before this loop\n // ever runs. Kept as a guard against the two tables drifting apart.\n /* c8 ignore start */\n if (!metadata) {\n warnings.push({\n code: 'UNKNOWN_TOKEN_NO_METADATA',\n message: `Token \"${piece.value}\" is recognized by the tokenizer but has no metadata entry in tokenMetadata.ts — this is a bug in temporal-fmt.`,\n });\n offset += piece.value.length;\n continue;\n }\n /* c8 ignore stop */\n tokens.push({ name: piece.value, position: offset, metadata });\n\n // Required fields: read off the handler map (the source of truth\n // for \"what field does this token need\"). Falls back to the\n // metadata's supportedTypes-derived field set if the handler isn't\n // found, which also shouldn't happen.\n const handler = _handlerFor(piece.value);\n if (handler) {\n requiredFields.add(handler.field as string);\n }\n\n // compatibleTypes: intersect every token's supportedTypes. The\n // first token seeds the set; subsequent tokens narrow it.\n if (tokens.length === 1) {\n for (const t of metadata.supportedTypes) compatibleTypesSet.add(t);\n } else {\n const stillCompatible = new Set<TemporalType>();\n for (const t of compatibleTypesSet) {\n if (metadata.supportedTypes.includes(t)) stillCompatible.add(t);\n }\n compatibleTypesSet.clear();\n for (const t of stillCompatible) compatibleTypesSet.add(t);\n }\n\n if (!metadata.parseCapable) {\n parseable = false;\n warnings.push({\n code: 'FORMAT_ONLY_TOKEN',\n message: `Token \"${piece.value}\" is format-only — it can't be parsed back into a value. Use a parse-capable variant (e.g. \"d\" instead of \"do\") if you need round-trip.`,\n });\n }\n if (metadata.localeSensitive) localeSensitive = true;\n if (metadata.calendarSensitive) calendarSensitive = true;\n if (metadata.timezoneSensitive) timezoneSensitive = true;\n if (!metadata.roundTripSafe) roundTripSafe = false;\n\n // Track runs of adjacent unpadded numeric tokens — same definition\n // as parsePattern.ts uses (UNPADDED_NUMERIC_TOKENS). A literal\n // between tokens breaks the run; this is checked by the flushRun\n // call above.\n if (UNPADDED_NUMERIC_TOKENS.has(piece.value)) {\n unpaddedRun.push(piece.value);\n } else {\n flushRun();\n }\n\n offset += piece.value.length;\n }\n flushRun();\n\n // Cross-token checks the runtime parse() performs. Mirroring them\n // here lets the ESLint plugin flag them statically without having\n // to re-derive the logic.\n const tokenNames = tokens.map((t) => t.name);\n const has12Hour = tokenNames.some((t) => t === 'hh' || t === 'h');\n const has24Hour = tokenNames.some((t) => t === 'HH' || t === 'H');\n const hasAPeriod = tokenNames.some((t) => t === 'a');\n if (has12Hour && !hasAPeriod) {\n warnings.push({\n code: 'TWELVE_HOUR_WITHOUT_A',\n message: '12-hour token (\"hh\"/\"h\") used without an \"a\" (AM/PM) token — parse() can\\'t tell AM from PM and throws at runtime.',\n });\n }\n if (has12Hour && has24Hour) {\n warnings.push({\n code: 'MIXED_12_AND_24_HOUR',\n message: 'Mixing 24-hour and 12-hour tokens — parse() refuses to guess which is authoritative and throws at runtime.',\n });\n }\n\n const hasOffset = tokenNames.some((t) => t === 'X' || t === 'XX' || t === 'XXX' || t === 'x' || t === 'xx' || t === 'xxx');\n const hasZzz = tokenNames.some((t) => t === 'zzz');\n if (hasOffset && hasZzz) {\n warnings.push({\n code: 'ZZZ_WITH_OFFSET_TOKEN',\n message: 'Format string has both \"zzz\" and an offset token — parse() cross-checks them and throws if the parsed offset disagrees with the zone\\'s actual offset.',\n });\n }\n if (hasOffset && !requiredFields.has('year')) {\n warnings.push({\n code: 'OFFSET_WITHOUT_FULL_DATE',\n message: 'Offset token used without a full date — parse() needs a complete date and time to build a ZonedDateTime and throws at runtime otherwise.',\n });\n }\n\n return {\n tokens,\n requiredFields: [...requiredFields].sort(),\n compatibleTypes: [...compatibleTypesSet].sort(),\n parseable,\n localeSensitive,\n calendarSensitive,\n timezoneSensitive,\n // Ambiguous iff at least one warning fired about an ambiguous run.\n ambiguous: warnings.some((w) => w.code === 'AMBIGUOUS_NUMERIC_RUN'),\n roundTripSafe: roundTripSafe && parseable,\n warnings,\n };\n}\n\n// explainFormat: human-readable rendering of analyzeFormat's output.\n// Used by the CLI (section AD) for `temporal-fmt inspect <fmt>` and by\n// the IDE for hover docs on a format string. Plain prose, no JSON —\n// callers wanting machine-readable output should call analyzeFormat()\n// directly.\nexport function explainFormat(formatStr: string): string {\n const analysis = analyzeFormat(formatStr);\n const lines: string[] = [];\n lines.push(`Format string: \"${formatStr}\"`);\n lines.push(`Tokens (${analysis.tokens.length}):`);\n for (const t of analysis.tokens) {\n lines.push(` ${t.name} @${t.position} — ${t.metadata.meaning}`);\n }\n if (analysis.requiredFields.length > 0) {\n lines.push(`Required fields: ${analysis.requiredFields.join(', ')}`);\n }\n if (analysis.compatibleTypes.length > 0) {\n lines.push(`Compatible Temporal types: ${analysis.compatibleTypes.join(', ')}`);\n }\n lines.push(`Parseable: ${analysis.parseable ? 'yes' : 'no'}`);\n lines.push(`Locale-sensitive: ${analysis.localeSensitive ? 'yes' : 'no'}`);\n lines.push(`Calendar-sensitive: ${analysis.calendarSensitive ? 'yes' : 'no'}`);\n lines.push(`Timezone-sensitive: ${analysis.timezoneSensitive ? 'yes' : 'no'}`);\n lines.push(`Ambiguous: ${analysis.ambiguous ? 'yes' : 'no'}`);\n lines.push(`Round-trip safe: ${analysis.roundTripSafe ? 'yes' : 'no'}`);\n if (analysis.warnings.length > 0) {\n lines.push('Warnings:');\n for (const w of analysis.warnings) {\n lines.push(` [${w.code}] ${w.message}`);\n }\n }\n return lines.join('\\n');\n}\n\n// isValidFormat: true iff tokenize() accepts the string without\n// throwing. Distinct from analyzeFormat's `parseable` flag — a format\n// string can be valid (tokenizes cleanly) but not parseable (contains\n// a format-only token like `do`). Both checks are exposed since callers\n// want different things: validity for \"will format() throw\", parseability\n// for \"will parse() throw\".\nexport function isValidFormat(formatStr: string): boolean {\n if (formatStr.length > MAX_FORMAT_LENGTH) return false;\n try {\n tokenize(formatStr);\n return true;\n } catch {\n return false;\n }\n}\n\n// validateFormat: throws on invalid format strings instead of\n// returning a boolean. Mirrors the existing strict-validation\n// convention (descriptive thrown errors, never silent failures).\n// Returns the analysis for callers who want both — validate-then-use.\nexport function validateFormat(formatStr: string): FormatAnalysis {\n const analysis = analyzeFormat(formatStr);\n return analysis;\n}\n\n// Exported for callers that want to query the token table without\n// touching internals. Kept here rather than re-exported from\n// tokenMetadata.ts so this module is the single import for \"anything\n// analyzer-related.\"\nexport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\n\n// Re-export FORMAT_ONLY_TOKENS so callers don't have to import from\n// pattern.ts (which is a lower-level module they shouldn't need to\n// know about).\nexport { FORMAT_ONLY_TOKENS } from './pattern.js';\n\n// Used by callers that need to look up which field a token requires —\n// e.g. the ESLint plugin's \"type mismatch\" check (section Y) wants to\n// answer \"does the type the caller passed to format() carry this\n// field?\" without having to actually call format() and catch the\n// thrown error. Mirrors the same lookup format.ts does internally.\nexport function fieldForToken(token: string): keyof TemporalLike | undefined {\n return _handlerFor(token)?.field;\n}","// Calendar utility helpers. These are pure functions\n// over the TemporalLike shape — no Temporal namespace needed, same\n// approach as isoWeek.ts and the field-reading helpers in format.ts.\n// Letting callers compute dayOfYear/weekOfYear/etc. without going\n// through format() means they can build their own derived values\n// without committing to a string format.\n//\n// Calendar-sensitivity: the helpers in this module assume the\n// iso8601 (Gregorian) calendar — that's what TemporalLike fields\n// carry for the overwhelming majority of callers. Non-Gregorian\n// calendars (hebrew, islamic, etc.) need their own helpers; this\n// module doesn't try to be calendar-polymorphic the way Temporal\n// itself is. Documented limitation, not a design choice — see\n// VERIFICATION.md for the rationale.\n\nimport { isGregorianLeapYear, dayOfYear, isoWeekYearAndWeek } from './isoWeek.js';\n\n// A subset of TemporalLike that has the date fields these helpers need,\n// plus optional time fields. PlainTime isn't a DateFieldView (no\n// year/month/day), but PlainDateTime / ZonedDateTime / PlainDate all\n// match. Time fields are optional so callers can pass a PlainDate\n// to startOf(value, 'month') without having to populate hour/minute/etc.\n// Exported so the comparison/arithmetic modules can use the same\n// narrowing.\nexport interface DateFieldView {\n year?: number;\n month?: number;\n day?: number;\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n dayOfWeek?: number;\n calendarId?: string;\n}\n\nfunction requireFields(view: DateFieldView, fields: Array<keyof DateFieldView>): void {\n for (const f of fields) {\n if (typeof view[f] !== 'number') {\n throw new Error(\n `temporal-fmt: calendar helper requires \"${String(f)}\", which this value doesn't have. ` +\n `Pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n }\n}\n\nexport function daysInMonth(view: DateFieldView): number {\n requireFields(view, ['year', 'month']);\n const { year, month } = view;\n // Standard Gregorian month lengths. February's length depends on\n // whether `year` is a leap year — the same isGregorianLeapYear check\n // isoWeek.ts uses for dayOfYear arithmetic.\n const LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n if (month === 2 && isGregorianLeapYear(year!)) return 29;\n return LENGTHS[(month! - 1)!]!;\n}\n\nexport function daysInYear(view: DateFieldView): 365 | 366 {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!) ? 366 : 365;\n}\n\nexport function monthsInYear(_view: DateFieldView): 12 {\n // Gregorian always has 12 months. Other calendars (Hebrew leap years\n // have 13) need calendar-aware logic this module doesn't carry — see\n // the file-level comment. The `_view` parameter is kept so the\n // signature mirrors the other helpers and a future calendar-aware\n // implementation can use it without changing call sites.\n return 12;\n}\n\nexport function isLeapYear(view: DateFieldView): boolean {\n requireFields(view, ['year']);\n return isGregorianLeapYear(view.year!);\n}\n\n// `isLeapMonth` would require knowing which month of a leap-year-aware\n// calendar is the leap month — Gregorian doesn't have one, so this\n// returns false unconditionally. Kept here so the public surface\n// matches the module's listing; non-Gregorian calendars need\n// a different implementation.\nexport function isLeapMonth(_view: DateFieldView): boolean {\n return false;\n}\n\nexport function dayOfYearHelper(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day']);\n return dayOfYear(view.year!, view.month!, view.day!);\n}\n\n// ISO 8601 week and week-year. Delegates to isoWeek.ts's\n// isoWeekYearAndWeek, which does the full Thursday-of-week\n// computation to handle the year-boundary cases (Dec 29-31 belonging\n// to week 1 of next year, Jan 1-3 belonging to week 52/53 of the\n// previous year).\nexport function weekOfYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).week;\n}\n\nexport function weekYear(view: DateFieldView): number {\n requireFields(view, ['year', 'month', 'day', 'dayOfWeek']);\n return isoWeekYearAndWeek(view.year!, view.month!, view.day!, view.dayOfWeek!).isoYear;\n}\n\n/**\n * Fiscal-quarter options. `startMonth` is the calendar month (1-12) the\n * fiscal year begins on — e.g. `7` for a fiscal year starting in July.\n * Omitted or `1` gives the calendar-quarter behavior getQuarter() has\n * always had (Jan-Mar = Q1, etc.), so existing callers passing nothing\n * see no change.\n */\nexport interface QuarterOptions {\n startMonth?: number;\n}\n\nfunction validateStartMonth(startMonth: number): void {\n if (!Number.isInteger(startMonth) || startMonth < 1 || startMonth > 12) {\n throw new Error(\n `temporal-fmt: getQuarter's startMonth must be an integer from 1 to 12 (got ${startMonth}).`\n );\n }\n}\n\nexport function getQuarter(view: DateFieldView, options: QuarterOptions = {}): number {\n requireFields(view, ['month']);\n const startMonth = options.startMonth ?? 1;\n validateStartMonth(startMonth);\n if (startMonth === 1) {\n // Mirrors the Q token: months 1-3 → Q1, 4-6 → Q2, 7-9 → Q3, 10-12 → Q4.\n return Math.ceil(view.month! / 3);\n }\n // Fiscal case: shift the month so startMonth becomes month 1 of the\n // fiscal year (mod 12, 1-indexed), then apply the same ceil(/3) rule.\n // E.g. startMonth=7 (fiscal year starts July): July→1, Aug→2, ...,\n // Dec→6, Jan→7, ..., June→12. Then Q1 = fiscal months 1-3 (Jul-Sep),\n // matching the common \"FY starts in July\" convention where Q1 is the\n // first quarter of the fiscal year, not a quarter numbered by which\n // calendar quarter it falls in.\n const shifted = ((view.month! - startMonth + 12) % 12) + 1;\n return Math.ceil(shifted / 3);\n}\n\n// `getMonth` / `getWeekday` look trivial (just read the field) but\n// they're kept as real functions for API surface consistency with the\n// rest of this module. They also normalize: getWeekday returns 1-7\n// (Mon-Sun, matching Temporal's spec) regardless of what numbering the\n// caller's underlying value uses.\nexport function getMonth(view: DateFieldView): number {\n requireFields(view, ['month']);\n return view.month!;\n}\n\nexport function getWeekday(view: DateFieldView): number {\n requireFields(view, ['dayOfWeek']);\n return view.dayOfWeek!;\n}\n\n// startOf / endOf return new field bags (not Temporal objects — this\n// module is polyfill-free) with the relevant fields zeroed/extended.\n// Callers can pass the result to a Temporal constructor if they want\n// a typed value.\nexport type StartOfUnit = 'day' | 'month' | 'year' | 'hour' | 'minute' | 'second';\n\n// Returns true if the given unit (when used with startOf/endOf) should\n// also touch the time fields. 'day', 'month', 'year' all imply a\n// resolution coarser than an hour, so startOf zeroes the time fields\n// and endOf maxes them. Sub-hour units (hour/minute/second) only touch\n// the fields finer than themselves.\nfunction touchesTime(unit: StartOfUnit): boolean {\n return unit === 'day' || unit === 'month' || unit === 'year';\n}\n\n// startOf/endOf reassign year/month/day, which invalidates any\n// dayOfWeek carried over from the input — a plain { ...view } spread\n// leaves the old value sitting there unchanged. Same failure mode\n// businessCalendar.ts's isBusinessDay() works around for add(); we\n// recompute here rather than trust the copied field.\nfunction recomputeDayOfWeek(view: DateFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: startOf()/endOf() both call\n * asDateFieldView() before this, which already throws if year/month/day\n * aren't all numbers — so by the time a value with a numeric dayOfWeek\n * reaches here, year/month/day are guaranteed present too. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\nexport function startOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 1;\n result.day = 1;\n } else if (unit === 'month') {\n result.day = 1;\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 0;\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'hour') {\n result.minute = 0;\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'minute') {\n result.second = 0;\n result.millisecond = 0;\n } else if (unit === 'second') {\n result.millisecond = 0;\n }\n return result;\n}\n\nexport function endOf(value: unknown, unit: StartOfUnit): DateFieldView {\n const view = asDateFieldView(value);\n const result: DateFieldView = { ...view };\n if (unit === 'year') {\n result.month = 12;\n result.day = daysInMonth({ year: result.year!, month: 12 });\n } else if (unit === 'month') {\n result.day = daysInMonth({ year: result.year!, month: result.month! });\n }\n recomputeDayOfWeek(result);\n if (touchesTime(unit)) {\n result.hour = 23;\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'hour') {\n result.minute = 59;\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'minute') {\n result.second = 59;\n result.millisecond = 999;\n } else if (unit === 'second') {\n result.millisecond = 999;\n }\n return result;\n}\n\n// Type-narrowing helpers used by the comparison/arithmetic modules.\n// Lets them accept any of the four date-carrying Temporal types without\n// importing Temporal itself.\nexport function asDateFieldView(value: unknown): DateFieldView {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: expected a date-carrying Temporal value, got ${String(value)}.`);\n }\n // Temporal instances expose year/month/day/etc. as prototype getters,\n // not own enumerable properties — so `{ ...value }` would lose them.\n // Read them explicitly. Only the fields actually present on this\n // value type end up in the returned view.\n const v = value as Record<string, unknown>;\n const out: DateFieldView = {};\n if (typeof v.year === 'number') out.year = v.year;\n if (typeof v.month === 'number') out.month = v.month;\n if (typeof v.day === 'number') out.day = v.day;\n if (typeof v.hour === 'number') out.hour = v.hour;\n if (typeof v.minute === 'number') out.minute = v.minute;\n if (typeof v.second === 'number') out.second = v.second;\n if (typeof v.millisecond === 'number') out.millisecond = v.millisecond;\n if (typeof v.dayOfWeek === 'number') out.dayOfWeek = v.dayOfWeek;\n if (typeof v.calendarId === 'string') out.calendarId = v.calendarId;\n if (out.year === undefined || out.month === undefined || out.day === undefined) {\n throw new Error(\n `temporal-fmt: value is missing year/month/day fields — pass a Temporal.PlainDate / PlainDateTime / ZonedDateTime.`\n );\n }\n return out;\n}\n\n// Re-export the TemporalType alias so callers can import everything\n// from one place.\nexport type { TemporalType } from './tokenMetadata.js';\n// Re-export TemporalLike for the same reason.\nexport type { TemporalLike } from './tokens.js';","// Date arithmetic helpers. Pure functions returning\n// field bags, same convention as calendarUtils.ts/comparison.ts — no\n// Temporal namespace needed. Callers can pass the result back into a\n// Temporal constructor to get a typed value, or chain operations\n// without committing to a Temporal implementation.\n\nimport { asDateFieldView, daysInMonth, type DateFieldView } from './calendarUtils.js';\n\nexport type AddUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction asDateTime(value: unknown): DateTimeFieldView {\n return asDateFieldView(value) as DateTimeFieldView;\n}\n\n// Recomputes dayOfWeek after year/month/day change. Mirrors\n// calendarUtils.ts's recomputeDayOfWeek — same fix, needed here too\n// since add()/subtract() mutate the date fields via a shallow spread\n// that otherwise leaves the old dayOfWeek sitting there, silently wrong.\nfunction recomputeDayOfWeek(view: DateTimeFieldView): void {\n if (typeof view.dayOfWeek !== 'number') return;\n /* c8 ignore start @preserve -- unreachable: every call site (add()'s\n 'years'/'months' cases, shiftDays()) passes a `result` whose\n year/month/day were just explicitly set, and the value originally\n came from asDateTime()/asDateFieldView(), which throws unless\n year/month/day are all present as numbers. So by the time this guard\n runs, all three are guaranteed valid. Kept in case a future caller\n constructs a DateTimeFieldView bypassing that validation. */\n if (typeof view.year !== 'number' || typeof view.month !== 'number' || typeof view.day !== 'number') return;\n /* c8 ignore stop @preserve */\n const jsDow = new Date(Date.UTC(view.year, view.month - 1, view.day)).getUTCDay(); // 0=Sun..6=Sat\n view.dayOfWeek = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n}\n\n// Adds the requested amount to the value's specified unit. Returns a\n// new field bag; the input is not mutated. Handles month/year overflow\n// by clamping to the last valid day of the target month (Feb 29 + 1\n// year on a non-leap year → Feb 28) — Temporal's `constrain` overflow\n// mode, which is the closest match to \"do the obvious thing\" callers\n// expect from arithmetic.\nexport function add(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n const v = asDateTime(value);\n let result: DateTimeFieldView = { ...v };\n switch (unit) {\n case 'years': {\n /* c8 ignore start @preserve -- the `?? 0` fallback is unreachable:\n asDateTime()/asDateFieldView() (called at the top of add()) throws\n unless year is present as a number, so result.year is never\n undefined here. Kept as a guard against a future change that\n relaxes that validation. */\n result.year = (result.year ?? 0) + amount;\n /* c8 ignore stop @preserve */\n // Clamp day-of-month to the new month's length.\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'months': {\n // Total months = year*12 + month + amount, then split back.\n /* c8 ignore start @preserve -- both `??` fallbacks are unreachable\n for the same reason as the 'years' case above: asDateFieldView()\n guarantees year and month are present numbers before add() ever\n reaches here. */\n const total = (result.year ?? 0) * 12 + (result.month ?? 1) - 1 + amount;\n /* c8 ignore stop @preserve */\n result.year = Math.floor(total / 12);\n result.month = (total % 12) + 1;\n if (result.month < 1) { result.month += 12; result.year -= 1; }\n // Negative modulo handling: when total < 0, the mod goes negative.\n // Re-normalize.\n /* c8 ignore start @preserve -- unreachable: (total % 12) + 1 can never\n exceed 12 for any integer total in JS (its range is [-10, 12] since\n JS's % can return a negative remainder but never one with\n magnitude >= 12, so adding 1 tops out at 12). Only the < 1 branch\n above is reachable; this was written symmetrically with it but the\n overflow direction it guards against can't occur. Kept rather than\n removed since it costs nothing and documents the intent. */\n if (result.month > 12) { result.month -= 12; result.year += 1; }\n /* c8 ignore stop @preserve */\n const maxDay = daysInMonth({ year: result.year!, month: result.month! });\n if (result.day! > maxDay) result.day = maxDay;\n recomputeDayOfWeek(result);\n break;\n }\n case 'weeks':\n result = shiftDays(result, amount * 7);\n break;\n case 'days':\n result = shiftDays(result, amount);\n break;\n case 'hours':\n result = shiftTime(result, amount * 3_600_000);\n break;\n case 'minutes':\n result = shiftTime(result, amount * 60_000);\n break;\n case 'seconds':\n result = shiftTime(result, amount * 1_000);\n break;\n case 'milliseconds':\n result = shiftTime(result, amount);\n break;\n }\n return result;\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n // Convert Y/M/D to a day count, add, convert back. Using the same\n // algorithm as comparison.ts's sameWeek helper — proleptic Gregorian\n // day count via Howard Hinnant's era-based formula.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n\n // Invert: totalDays → Y/M/D. Same source (Howard Hinnant's days_from_civil).\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n\n const result: DateTimeFieldView = { ...v, year: yOut, month: m2out, day: d2 };\n recomputeDayOfWeek(result);\n return result;\n}\n\nfunction shiftTime(v: DateTimeFieldView, msDelta: number): DateTimeFieldView {\n // Convert all time fields to ms, add, then split back. Day overflow\n // propagates to shiftDays; sub-second precision is preserved (caller\n // asking for hours/minutes/seconds/milliseconds gets ms-resolution\n // arithmetic, which is the limit of these helpers — nanoseconds\n // aren't supported here, only via Temporal.Duration's own add).\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n const totalMs = (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0)\n + msDelta;\n\n // Split into day-overflow + within-day ms.\n const dayOverflow = Math.floor(totalMs / MS_PER_DAY);\n let withinDay = totalMs % MS_PER_DAY;\n if (withinDay < 0) { withinDay += MS_PER_DAY; }\n\n const hour = Math.floor(withinDay / MS_PER_HOUR);\n const minute = Math.floor((withinDay % MS_PER_HOUR) / MS_PER_MINUTE);\n const second = Math.floor((withinDay % MS_PER_MINUTE) / MS_PER_SECOND);\n const millisecond = withinDay % MS_PER_SECOND;\n\n const result: DateTimeFieldView = { ...v, hour, minute, second, millisecond };\n if (dayOverflow !== 0) {\n return shiftDays(result, dayOverflow);\n }\n return result;\n}\n\nexport function subtract(value: unknown, amount: number, unit: AddUnit): DateTimeFieldView {\n return add(value, -amount, unit);\n}\n\n// Per-unit convenience wrappers — matches the grouping of\n// addYears / addMonths / ... / subtractNanoseconds. Nanosecond variants\n// aren't provided here because these helpers operate on field bags, and\n// the millisecond/microsecond/nanosecond split isn't preserved through\n// arithmetic at sub-millisecond precision the way Temporal.Duration's\n// own add preserves it. For ns-precision arithmetic, use Temporal.Duration\n// directly.\nexport const addYears = (v: unknown, n: number) => add(v, n, 'years');\nexport const addMonths = (v: unknown, n: number) => add(v, n, 'months');\nexport const addWeeks = (v: unknown, n: number) => add(v, n, 'weeks');\nexport const addDays = (v: unknown, n: number) => add(v, n, 'days');\nexport const addHours = (v: unknown, n: number) => add(v, n, 'hours');\nexport const addMinutes = (v: unknown, n: number) => add(v, n, 'minutes');\nexport const addSeconds = (v: unknown, n: number) => add(v, n, 'seconds');\nexport const addMilliseconds = (v: unknown, n: number) => add(v, n, 'milliseconds');\n\nexport const subtractYears = (v: unknown, n: number) => subtract(v, n, 'years');\nexport const subtractMonths = (v: unknown, n: number) => subtract(v, n, 'months');\nexport const subtractWeeks = (v: unknown, n: number) => subtract(v, n, 'weeks');\nexport const subtractDays = (v: unknown, n: number) => subtract(v, n, 'days');\nexport const subtractHours = (v: unknown, n: number) => subtract(v, n, 'hours');\nexport const subtractMinutes = (v: unknown, n: number) => subtract(v, n, 'minutes');\nexport const subtractSeconds = (v: unknown, n: number) => subtract(v, n, 'seconds');\nexport const subtractMilliseconds = (v: unknown, n: number) => subtract(v, n, 'milliseconds');\n\n// difference(): returns the count of `unit` boundaries between two values.\n// Pure-integer math, no Temporal.Duration involved. For sub-day\n// precision the result is an integer count of the requested unit (not\n// a Duration field bag) — matches the most common caller expectation\n// (\"how many days between these two dates?\").\nexport type DiffUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds';\n\nexport function difference(a: unknown, b: unknown, unit: DiffUnit): number {\n const av = asDateTime(a);\n const bv = asDateTime(b);\n\n // For day-granular units (years/months/weeks/days), compute via\n // calendar arithmetic on the date fields. For sub-day units, compute\n // via total-ms and divide.\n if (unit === 'years') {\n return bv.year! - av.year!;\n }\n if (unit === 'months') {\n return (bv.year! * 12 + bv.month! - 1) - (av.year! * 12 + av.month! - 1);\n }\n if (unit === 'weeks' || unit === 'days') {\n const aDays = toDayCount(av);\n const bDays = toDayCount(bv);\n const days = bDays - aDays;\n return unit === 'weeks' ? Math.trunc(days / 7) : days;\n }\n\n // Sub-day units: total-ms math.\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n const aMs = toTotalMs(av);\n const bMs = toTotalMs(bv);\n const diffMs = bMs - aMs;\n switch (unit) {\n case 'hours': return Math.trunc(diffMs / MS_PER_HOUR);\n case 'minutes': return Math.trunc(diffMs / MS_PER_MINUTE);\n case 'seconds': return Math.trunc(diffMs / MS_PER_SECOND);\n case 'milliseconds': return diffMs;\n }\n}\n\nfunction toDayCount(v: DateTimeFieldView): number {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\nfunction toTotalMs(v: DateTimeFieldView): number {\n const MS_PER_DAY = 86_400_000;\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n return toDayCount(v) * MS_PER_DAY\n + (v.hour ?? 0) * MS_PER_HOUR\n + (v.minute ?? 0) * MS_PER_MINUTE\n + (v.second ?? 0) * MS_PER_SECOND\n + (v.millisecond ?? 0);\n}\n\n// Per-unit difference wrappers — same convenience pattern as the add*\n// wrappers above.\nexport const differenceInYears = (a: unknown, b: unknown) => difference(a, b, 'years');\nexport const differenceInMonths = (a: unknown, b: unknown) => difference(a, b, 'months');\nexport const differenceInWeeks = (a: unknown, b: unknown) => difference(a, b, 'weeks');\nexport const differenceInDays = (a: unknown, b: unknown) => difference(a, b, 'days');\nexport const differenceInHours = (a: unknown, b: unknown) => difference(a, b, 'hours');\nexport const differenceInMinutes = (a: unknown, b: unknown) => difference(a, b, 'minutes');\nexport const differenceInSeconds = (a: unknown, b: unknown) => difference(a, b, 'seconds');\nexport const differenceInMilliseconds = (a: unknown, b: unknown) => difference(a, b, 'milliseconds');","// Comparison helpers. Pure functions over the field\n// shape, no Temporal namespace needed — same convention as\n// calendarUtils.ts. All comparisons are field-based, not identity-based,\n// so a polyfill PlainDate compares equal to a native PlainDate with the\n// same year/month/day.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\nfunction toComparableMs(view: DateTimeFieldView): number {\n // Exact proleptic-Gregorian day count (Howard Hinnant's days_from_civil,\n // same algorithm arithmetic.ts/interval.ts use) plus the time-of-day —\n // NOT an approximation. The previous implementation multiplied the\n // year offset by an average 365.2425 days/year, on the theory that the\n // error \"is constant for any given year, so it cancels out in a diff\".\n // That's only true when both dates fall in the same year: the year\n // term's error differs by up to ~0.76 days between adjacent years\n // (365.2425 vs 365/366 actual), which corrupts cross-year comparisons\n // near the boundary — e.g. isAfter(2025-01-01T00:00, 2024-12-31T18:12)\n // returned false (truth: ~5.8h after), and isEqual() returned true for\n // instants ~18h apart (2025-01-01T00:00 vs 2024-12-31T05:49:12).\n // Exact arithmetic has no such error term.\n const y = view.year!, m = view.month!, d = view.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n\n const MS_PER_HOUR = 3_600_000;\n const MS_PER_MINUTE = 60_000;\n const MS_PER_SECOND = 1_000;\n\n return days * 86_400_000\n + (view.hour ?? 0) * MS_PER_HOUR\n + (view.minute ?? 0) * MS_PER_MINUTE\n + (view.second ?? 0) * MS_PER_SECOND\n + (view.millisecond ?? 0);\n}\n\nfunction asDateTimeFieldView(value: unknown): DateTimeFieldView {\n const v = asDateFieldView(value);\n return v as DateTimeFieldView;\n}\n\n// Returns < 0 if a < b, 0 if a === b, > 0 if a > b. Same convention as\n// Array.prototype.sort and Temporal.PlainDate.compare. Lets callers\n// pass this directly as a comparator.\nexport function compare(a: unknown, b: unknown): number {\n const av = asDateTimeFieldView(a);\n const bv = asDateTimeFieldView(b);\n const am = toComparableMs(av);\n const bm = toComparableMs(bv);\n if (am < bm) return -1;\n if (am > bm) return 1;\n return 0;\n}\n\nexport function isEqual(a: unknown, b: unknown): boolean {\n return compare(a, b) === 0;\n}\n\nexport function isBefore(a: unknown, b: unknown): boolean {\n return compare(a, b) < 0;\n}\n\nexport function isAfter(a: unknown, b: unknown): boolean {\n return compare(a, b) > 0;\n}\n\nexport function min(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: min() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) < 0) best = values[i];\n }\n return best;\n}\n\nexport function max(values: unknown[]): unknown {\n if (!Array.isArray(values) || values.length === 0) {\n throw new Error('temporal-fmt: max() requires a non-empty array of values.');\n }\n let best = values[0];\n for (let i = 1; i < values.length; i++) {\n if (compare(values[i], best) > 0) best = values[i];\n }\n return best;\n}\n\nexport function clamp(value: unknown, lo: unknown, hi: unknown): unknown {\n if (compare(value, lo) < 0) return lo;\n if (compare(value, hi) > 0) return hi;\n return value;\n}\n\nexport function isBetween(value: unknown, lo: unknown, hi: unknown): boolean {\n return compare(value, lo) >= 0 && compare(value, hi) <= 0;\n}\n\n// Semantic helpers — same-day/week/month/quarter/year tests. Useful for\n// UI (\"is this event today?\") and for grouping/aggregation. All take a\n// reference value to compare against; the \"isToday\" / \"isTomorrow\" /\n// \"isYesterday\" variants compute the reference from the current system\n// date themselves so callers don't have to.\nfunction readYearMonthDay(value: unknown): { year: number; month: number; day: number } {\n const v = asDateFieldView(value);\n return { year: v.year!, month: v.month!, day: v.day! };\n}\n\nfunction todayYearMonthDay(): { year: number; month: number; day: number } {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };\n}\n\nfunction dateEqualYearMonthDay(a: { year: number; month: number; day: number }, b: { year: number; month: number; day: number }): boolean {\n return a.year === b.year && a.month === b.month && a.day === b.day;\n}\n\nexport function isSameDay(value: unknown, reference: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), readYearMonthDay(reference));\n}\n\nexport function isToday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), todayYearMonthDay());\n}\n\nfunction tomorrowYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() + 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nfunction yesterdayYearMonthDay(): { year: number; month: number; day: number } {\n const d = new Date();\n d.setDate(d.getDate() - 1);\n return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };\n}\n\nexport function isTomorrow(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), tomorrowYearMonthDay());\n}\n\nexport function isYesterday(value: unknown): boolean {\n return dateEqualYearMonthDay(readYearMonthDay(value), yesterdayYearMonthDay());\n}\n\nfunction sameWeek(yearA: number, monthA: number, dayA: number, yearB: number, monthB: number, dayB: number): boolean {\n // Same week iff the dates are within 7 days of each other AND the earlier\n // one's weekday is ≤ the later one's weekday (so we don't say \"Sun and\n // the following Mon are in the same week\" — ISO weeks run Mon-Sun).\n // Easier: compute ISO week numbers via isoWeekYearAndWeek, compare both\n // year and week. That handles the year-boundary case (Dec 31 vs Jan 1 of\n // the next year, which can be in the same ISO week).\n // Local import to avoid module-cycle: isoWeek.ts doesn't import from here.\n // Lazy require-style dynamic via inline static import at top would be\n // cleaner; this inline computation is fine for a pure helper.\n // ...actually let's just compute weekday offsets directly. Same-ISO-week\n // iff |d1 - d2| < 7 days AND the earlier date's ISO weekday ≤ the later\n // date's ISO weekday so they fall in the same Mon-Sun span.\n const DAYS_PER_400_YEARS = 146097;\n const daysSince = (y: number, m: number, d: number): number => {\n // Zeller's congruence variant — convert Y/M/D to a day count.\n // Using a simple algorithm here that's correct for proleptic Gregorian.\n const y2 = m <= 2 ? y - 1 : y;\n const era = (y2 >= 0 ? y2 : y2 - 399) / 400 | 0;\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((Math.floor(153 * m2 + 2) / 5) + d - 1);\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * DAYS_PER_400_YEARS + doe - 719468;\n };\n const a = daysSince(yearA, monthA, dayA);\n const b = daysSince(yearB, monthB, dayB);\n if (Math.abs(a - b) >= 7) return false;\n // Same week iff both dates round to the same Monday. Compute the\n // Monday-of-week for each: subtract (weekday - 1) days, where weekday\n // is 1=Mon..7=Sun. Use the standard Zeller formula for weekday.\n const weekdayOf = (y: number, m: number, d: number): number => {\n const days = daysSince(y, m, d);\n // 1970-01-01 was a Thursday (ISO 4). So weekday = ((days + 3) mod 7) + 1\n // mapped to Mon=1..Sun=7. Let's compute: Sun=0..Sat=6 via JS Date.\n const date = new Date(Date.UTC(y, m - 1, d));\n const jsDow = date.getUTCDay(); // 0=Sun..6=Sat\n return jsDow === 0 ? 7 : jsDow;\n };\n const wa = a - (weekdayOf(yearA, monthA, dayA) - 1);\n const wb = b - (weekdayOf(yearB, monthB, dayB) - 1);\n return wa === wb;\n}\n\nexport function isSameWeek(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return sameWeek(a.year, a.month, a.day, b.year, b.month, b.day);\n}\n\nexport function isSameMonth(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && a.month === b.month;\n}\n\nexport function isSameQuarter(value: unknown, reference: unknown): boolean {\n const a = readYearMonthDay(value);\n const b = readYearMonthDay(reference);\n return a.year === b.year && Math.ceil(a.month / 3) === Math.ceil(b.month / 3);\n}\n\nexport function isSameYear(value: unknown, reference: unknown): boolean {\n return readYearMonthDay(value).year === readYearMonthDay(reference).year;\n}\n\n// isWeekend / isWeekday depend on dayOfWeek — present on PlainDate,\n// PlainDateTime, ZonedDateTime, but NOT on PlainTime. Callers passing\n// PlainTime get a descriptive throw. Doesn't call asDateFieldView\n// because that would reject PlainTime (no year/month/day) before\n// reaching the dayOfWeek check — and the error we want here is the\n// \"no dayOfWeek field\" one, not the \"no date fields\" one.\nexport function isWeekend(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n throw new Error(`temporal-fmt: isWeekend() expected a Temporal value, got ${String(value)}.`);\n }\n const v = value as { dayOfWeek?: unknown };\n if (typeof v.dayOfWeek !== 'number') {\n throw new Error(\n `temporal-fmt: isWeekend() needs a value with a dayOfWeek field (PlainDate / PlainDateTime / ZonedDateTime).`\n );\n }\n // 6=Sat, 7=Sun in Temporal's Mon=1..Sun=7 numbering.\n return v.dayOfWeek === 6 || v.dayOfWeek === 7;\n}\n\nexport function isWeekday(value: unknown): boolean {\n return !isWeekend(value);\n}\n","// Rounding helpers. Pure functions over field bags,\n// same convention as arithmetic.ts/calendarUtils.ts.\n\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\nimport { InvalidDurationError } from './errors.js';\n\ntype RoundingUnit = 'day' | 'hour' | 'minute' | 'second' | 'millisecond';\ntype RoundingMode = 'nearest' | 'floor' | 'ceil' | 'trunc';\n\ninterface DateTimeFieldView extends DateFieldView {\n hour?: number;\n minute?: number;\n second?: number;\n millisecond?: number;\n}\n\n// Ms-per-unit table. Used by round() to convert a field bag to ms,\n// apply the rounding mode, and convert back. Mirrors how add() in\n// arithmetic.ts handles sub-day arithmetic.\nconst MS_PER_UNIT: Record<RoundingUnit, number> = {\n day: 86_400_000,\n hour: 3_600_000,\n minute: 60_000,\n second: 1_000,\n millisecond: 1,\n};\n\nfunction applyMode(ms: number, mode: RoundingMode, increment: number): number {\n // All four modes operate on absolute value then re-apply the sign,\n // so negative values round symmetrically (floor of -1.5 → -2, not -1).\n // Matches Math.round semantics for 'nearest' and Temporal's rounding\n // for the other three.\n const sign = ms < 0 ? -1 : 1;\n const abs = Math.abs(ms);\n const stepped = abs / increment;\n let rounded: number;\n switch (mode) {\n case 'nearest': rounded = Math.round(stepped); break;\n case 'floor': rounded = Math.floor(stepped); break;\n case 'ceil': rounded = Math.ceil(stepped); break;\n case 'trunc': rounded = Math.trunc(stepped); break;\n }\n return sign * rounded * increment;\n}\n\nfunction toMs(v: DateTimeFieldView): number {\n // Returns ms-since-(Howard Hinnant epoch). Used internally — the\n // absolute value is meaningful only relative to the same epoch used\n // by fromMs.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n return days * MS_PER_UNIT.day\n + (v.hour ?? 0) * MS_PER_UNIT.hour\n + (v.minute ?? 0) * MS_PER_UNIT.minute\n + (v.second ?? 0) * MS_PER_UNIT.second\n + (v.millisecond ?? 0);\n}\n\nfunction fromMs(ms: number, base: DateTimeFieldView): DateTimeFieldView {\n // Convert absolute ms (on the same epoch as toMs) back to a field bag.\n // Walks the Howard Hinnant days_from_civil inverse inline.\n const MS_PER_DAY = MS_PER_UNIT.day;\n const totalDays = Math.floor(ms / MS_PER_DAY);\n let withinDay = ms - totalDays * MS_PER_DAY; // ms since midnight\n // Defensive floating-point guard: given totalDays = Math.floor(ms /\n // MS_PER_DAY), withinDay = ms - totalDays * MS_PER_DAY is\n // mathematically guaranteed non-negative (that's what Math.floor\n // division gives you), verified against extreme values including\n // Number.MIN_SAFE_INTEGER and sub-ms fractional noise near day\n // boundaries. Kept in case a future change to how ms is computed\n // upstream breaks that guarantee.\n /* c8 ignore next */\n if (withinDay < 0) withinDay += MS_PER_DAY;\n const hour = Math.floor(withinDay / MS_PER_UNIT.hour);\n const minute = Math.floor((withinDay % MS_PER_UNIT.hour) / MS_PER_UNIT.minute);\n const second = Math.floor((withinDay % MS_PER_UNIT.minute) / MS_PER_UNIT.second);\n const millisecond = withinDay % MS_PER_UNIT.second;\n // Convert totalDays back to year/month/day via Howard Hinnant's\n // civil_from_days. Same algorithm as arithmetic.ts's shiftDays.\n const z = totalDays + 719468;\n const era = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe = z - era * 146097;\n const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);\n const y2 = yoe + era * 400;\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));\n const mp = Math.floor((5 * doy + 2) / 153);\n const d = doy - Math.floor((153 * mp + 2) / 5) + 1;\n const m = mp < 10 ? mp + 3 : mp - 9;\n const year = m <= 2 ? y2 + 1 : y2;\n return { ...base, year, month: m, day: d, hour, minute, second, millisecond };\n}\n\nfunction shiftDays(v: DateTimeFieldView, days: number): DateTimeFieldView {\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const totalDays = era * 146097 + doe - 719468 + days;\n const z = totalDays + 719468;\n const era2 = Math.floor((z >= 0 ? z : z - 146096) / 146097);\n const doe2 = z - era2 * 146097;\n const yoe2 = Math.floor((doe2 - Math.floor(doe2 / 1460) + Math.floor(doe2 / 36524) - Math.floor(doe2 / 146096)) / 365);\n const y2out = yoe2 + era2 * 400;\n const doy2 = doe2 - (365 * yoe2 + Math.floor(yoe2 / 4) - Math.floor(yoe2 / 100));\n const mp = Math.floor((5 * doy2 + 2) / 153);\n const d2 = doy2 - Math.floor((153 * mp + 2) / 5) + 1;\n const m2out = mp < 10 ? mp + 3 : mp - 9;\n const yOut = m2out <= 2 ? y2out + 1 : y2out;\n return { ...v, year: yOut, month: m2out, day: d2 };\n}\n\nexport interface RoundOptions {\n unit: RoundingUnit;\n mode?: RoundingMode;\n roundingIncrement?: number;\n}\n\nexport function round(value: unknown, options: RoundOptions): DateTimeFieldView {\n const base = asDateFieldView(value) as DateTimeFieldView;\n const mode = options.mode ?? 'nearest';\n const increment = (options.roundingIncrement ?? 1) * MS_PER_UNIT[options.unit];\n if (increment <= 0) {\n throw new Error(`temporal-fmt: round() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);\n }\n const ms = toMs(base);\n const rounded = applyMode(ms, mode, increment);\n return fromMs(rounded, base);\n}\n\nexport function floor(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'floor', roundingIncrement });\n}\n\nexport function ceil(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'ceil', roundingIncrement });\n}\n\nexport function truncate(value: unknown, unit: RoundingUnit, roundingIncrement: number = 1): DateTimeFieldView {\n return round(value, { unit, mode: 'trunc', roundingIncrement });\n}\n\n// Duration rounding. Takes a duration field bag and rounds it to the\n// requested unit. Operates on the duration's own fields (years/months\n// for calendar-bound units, days/hours/minutes/seconds/ms/µs/ns for\n// absolute units). Mirrors Temporal.Duration.prototype.round's surface\n// — but operates on plain field bags rather than real Temporal.Duration\n// instances, same convention as the rest of this module set.\nexport interface DurationFields {\n years?: number;\n months?: number;\n weeks?: number;\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n microseconds?: number;\n nanoseconds?: number;\n}\n\n// Converts one absolute-unit field to nanoseconds, exactly. Integer\n// values keep the exact BigInt path (arbitrary magnitude). Fractional\n// values — which parseISODuration legitimately produces (\"P1.5D\") —\n// scale in floating point and are accepted only when the product is a\n// safe integer, so P1.5D balances to exactly 1 day + 12 hours. A\n// fractional value whose product can't be represented exactly (large\n// fractional day counts) throws the typed InvalidDurationError instead\n// of the opaque \"TypeError: Cannot convert 1.5 to a BigInt\" the old\n// BigInt(v) call produced. Shared by roundDuration (here) and the\n// duration.ts arithmetic helpers — duration.ts already imports from\n// this module, so exporting keeps the dependency direction acyclic.\nexport function fieldToNs(v: number, nsPer: bigint, field: string): bigint {\n if (Number.isInteger(v)) return BigInt(v) * nsPer;\n const scaled = v * Number(nsPer);\n if (Number.isSafeInteger(scaled)) return BigInt(scaled);\n throw new InvalidDurationError({\n reason: `field \"${field}\" (${v}) cannot be converted to nanoseconds exactly — the value is too large for a fractional unit; use smaller units`,\n });\n}\n\ntype DurationUnit = 'years' | 'months' | 'weeks' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' | 'microseconds' | 'nanoseconds';\n\nconst DURATION_UNIT_TO_NS: Record<DurationUnit, bigint> = {\n years: 0n, // calendar-bound, can't convert without relativeTo\n months: 0n, // same\n weeks: 0n, // same\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n};\n\n// Absolute units (days and below) can be rounded without a relativeTo\n// because their length is fixed in nanoseconds. Calendar-bound units\n// (years/months/weeks) can't — would need a Temporal.Duration round()\n// call with a relativeTo. This helper throws if the caller tries to\n// round to/from a calendar-bound unit, rather than silently producing\n// a wrong result.\nfunction isCalendarBound(unit: DurationUnit): boolean {\n return unit === 'years' || unit === 'months' || unit === 'weeks';\n}\n\nexport function roundDuration(duration: DurationFields, options: {\n unit: DurationUnit;\n mode?: RoundingMode;\n roundingIncrement?: number;\n}): DurationFields {\n const mode = options.mode ?? 'nearest';\n const increment = BigInt(options.roundingIncrement ?? 1);\n if (increment <= 0n) {\n throw new Error(`temporal-fmt: roundDuration() requires a positive roundingIncrement (got ${options.roundingIncrement}).`);\n }\n if (isCalendarBound(options.unit)) {\n throw new Error(\n `temporal-fmt: roundDuration() to \"${options.unit}\" requires a Temporal.Duration with a relativeTo — ` +\n `this helper operates on plain field bags without calendar context. Use Temporal.Duration.prototype.round() directly.`\n );\n }\n\n // Sum all absolute-unit contributions into total nanoseconds.\n let totalNs = 0n;\n for (const u of ['days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'] as DurationUnit[]) {\n const v = duration[u] ?? 0;\n totalNs += fieldToNs(v, DURATION_UNIT_TO_NS[u], u);\n }\n\n // Apply rounding on the absolute value, preserving sign (same as round()).\n const stepNs = increment * DURATION_UNIT_TO_NS[options.unit];\n const sign = totalNs < 0n ? -1n : 1n;\n const abs = totalNs < 0n ? -totalNs : totalNs;\n const stepped = abs / stepNs;\n const remainder = abs % stepNs;\n let rounded: bigint;\n switch (mode) {\n case 'nearest':\n // Banker's rounding would be the Temporal spec, but Math.round-style\n // half-up is what callers typically expect from a \"round\" function.\n // Going with half-up to match round() above and avoid surprising anyone\n // who reads the output.\n rounded = remainder * 2n >= stepNs ? stepped + 1n : stepped;\n break;\n case 'floor': rounded = stepped; break;\n case 'ceil': rounded = remainder > 0n ? stepped + 1n : stepped; break;\n case 'trunc': rounded = stepped; break;\n }\n const newTotalNs = sign * rounded * stepNs;\n\n // Distribute back into the largest unit ≤ the requested unit. This\n // produces a balanced duration (e.g. rounding 90s to minutes gives\n // 1m30s in the minute field, not 90s).\n const result: DurationFields = { ...duration };\n let remaining = newTotalNs;\n // Clear all absolute units, then re-populate from largest to smallest so\n // the balanced output has all its value concentrated at the target unit\n // and below.\n const unitsInOrder: DurationUnit[] = ['days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];\n const startIdx = unitsInOrder.indexOf(options.unit);\n // Units finer than the rounding target are zeroed: all their value was\n // folded into `remaining` above, and whatever survives rounding now\n // lives at the target unit and above. (Units coarser than the target\n // are re-written by the distribution loop below — their value is\n // already inside `remaining`, so the spread copy is overwritten with\n // the same number.)\n for (let i = startIdx + 1; i < unitsInOrder.length; i++) {\n result[unitsInOrder[i]!] = 0;\n }\n // Re-populate from the largest absolute unit down to the target, so the\n // result has all its value concentrated at the target unit and above.\n for (let i = 0; i <= startIdx; i++) {\n const u = unitsInOrder[i]!;\n const unitNs = DURATION_UNIT_TO_NS[u];\n // Dead by construction: unitsInOrder only lists days/hours/minutes/\n // seconds/milliseconds/microseconds/nanoseconds, none of which are\n // 0n in DURATION_UNIT_TO_NS (only years/months/weeks are, and those\n // never appear in this array). Kept in case unitsInOrder grows to\n // include a calendar-bound unit later.\n /* c8 ignore next */\n if (unitNs === 0n) continue;\n const count = remaining / unitNs;\n remaining -= count * unitNs;\n // Assign unconditionally (zero counts write 0): the units at and above\n // the target are fully re-derived from the rounded total, so any\n // original spread-copied value must be overwritten either way —\n // conditionally skipping zero counts would leave the caller's stale\n // value sitting in the field.\n result[u] = Number(count);\n }\n return result;\n}","// Serialization / interop helpers. Standardized\n// parse/format pairs for ISO 8601, RFC 3339, RFC 2822, HTTP-date,\n// plus epoch conversions to/from unix seconds/ms/µs/ns.\n//\n// All of these wrap the existing parse()/format() surface rather than\n// reimplementing the formats — same convention as the rest of the\n// library (one tokenizer, one parser, one constructor path). The\n// standard-format helpers are essentially pre-baked format strings\n// with the right tokens, plus stricter input validation where the\n// standard demands it (e.g. RFC 3339 requires UTC-offset-only zones,\n// not named IANA zones).\n\nimport { parse } from './parse.js';\nimport { format } from './format.js';\nimport { getTemporal, type TemporalNamespace } from './temporalProvider.js';\nimport { FormatSyntaxError, InvalidDateError } from './errors.js';\n\n// Build a Temporal instance from a field bag. Used by the parse helpers\n// to construct PlainDate / PlainDateTime / ZonedDateTime / Instant from\n// the fields the standard format carries. Throws on construction errors\n// (Feb 30, etc.) wrapped in InvalidDateError so callers get the typed\n// error surface.\nfunction constructFromFields(fields: Record<string, unknown>, kind: 'PlainDate' | 'PlainDateTime' | 'ZonedDateTime' | 'Instant'): unknown {\n const temporal = getTemporal();\n try {\n switch (kind) {\n case 'PlainDate':\n return temporal.PlainDate.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'PlainDateTime':\n return temporal.PlainDateTime.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'ZonedDateTime':\n return temporal.ZonedDateTime.from(fields as Record<string, number | string | undefined>, { overflow: 'reject' });\n case 'Instant': {\n // Instant.from takes an ISO string, not a field bag. Build the\n // ISO string ourselves and parse it.\n const iso = buildISOFromFields(fields);\n if (!temporal.Instant) {\n throw new Error('temporal-fmt: Temporal.Instant is not available in this implementation.');\n }\n return temporal.Instant.from(iso);\n }\n }\n } catch (err) {\n throw new InvalidDateError({ input: JSON.stringify(fields), reason: (err as Error).message });\n }\n}\n\nfunction buildISOFromFields(fields: Record<string, unknown>): string {\n const y = String(fields.year).padStart(4, '0');\n const m = String(fields.month).padStart(2, '0');\n const d = String(fields.day).padStart(2, '0');\n const h = fields.hour !== undefined ? `T${String(fields.hour).padStart(2, '0')}` : '';\n const min = fields.minute !== undefined ? `:${String(fields.minute).padStart(2, '0')}` : '';\n const sec = fields.second !== undefined ? `:${String(fields.second).padStart(2, '0')}` : '';\n const frac = fields.millisecond !== undefined && (fields.millisecond as number) > 0\n ? `.${String(fields.millisecond).padStart(3, '0')}`\n : '';\n const tz = typeof fields.timeZone === 'string' ? fields.timeZone : 'Z';\n return `${y}-${m}-${d}${h}${min}${sec}${frac}${tz}`;\n}\n\n// ISO 8601 is the most permissive of the standards here — accepts\n// calendar dates, week dates, ordinal dates, times with optional\n// fractional seconds, timezone offsets or 'Z'. Delegates to\n// Temporal's own ISO parsing (which is ISO 8601-compliant) by\n// constructing from the raw ISO string.\n\nexport function parseISO(input: string): unknown {\n const temporal = getTemporal();\n // Detect what kind of Temporal object the ISO string describes.\n // ISO 8601 strings have a date part, optional time part, optional\n // timezone. We dispatch to the right Temporal constructor based on\n // which parts are present.\n try {\n if (/^\\d{4}-\\d{2}-\\d{2}T/.test(input) || /T\\d{2}:\\d{2}/.test(input)) {\n // Has a time component.\n if (/[zZ]$/.test(input) || /[+-]\\d{2}:?\\d{2}$/.test(input)) {\n // ZonedDateTime. Temporal.ZonedDateTime.from accepts ISO with\n // a zone bracket (e.g. \"2026-08-04T15:45:30[UTC]\") but rejects\n // a bare offset with no bracket. For \"Z\" suffix, expand to\n // \"+00:00[UTC]\"; for a numeric offset, append that same offset\n // as the bracket (e.g. \"+05:30\" → \"+05:30[+05:30]\").\n let iso = input;\n if (/[zZ]$/.test(iso)) {\n iso = iso.slice(0, -1) + '+00:00[UTC]';\n } else {\n const offsetMatch = /([+-]\\d{2}:?\\d{2})$/.exec(iso);\n if (offsetMatch) {\n iso = `${iso}[${offsetMatch[1]}]`;\n }\n }\n return temporal.ZonedDateTime.from(iso as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n return temporal.PlainDateTime.from(input as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(input)) {\n return temporal.PlainDate.from(input as unknown as Record<string, number | string | undefined>, { overflow: 'reject' });\n }\n if (/^\\d{2}:\\d{2}/.test(input)) {\n return temporal.PlainTime.from(input as unknown as Record<string, number | string | undefined>);\n }\n throw new Error(`temporal-fmt: parseISO(\"${input}\") doesn't look like an ISO 8601 string.`);\n } catch (err) {\n throw new InvalidDateError({ input, reason: (err as Error).message });\n }\n}\n\nexport function formatISO(value: unknown): string {\n // Temporal objects expose toString() which produces ISO 8601 output\n // by spec. So formatting IS just toString().\n const v = value as { toString?: () => string };\n if (typeof v?.toString !== 'function') {\n throw new FormatSyntaxError({ reason: `formatISO() expects a Temporal value with toString(), got ${typeof value}.` });\n }\n return v.toString();\n}\n\n// RFC 3339 is a profile of ISO 8601 used in network protocols (HTTP,\n// JSON-RPC, etc.). Stricter than ISO 8601: requires full date+time,\n// requires seconds, requires UTC offset or 'Z', disallows IANA zone\n// names. Delegates to parseISO then validates the constraints.\n\nexport function parseRFC3339(input: string): unknown {\n // RFC 3339 grammar (https://www.rfc-editor.org/rfc/rfc3339#section-5.6):\n // date-fullyear = 4DIGIT\n // date-month = 2DIGIT ; 01-12\n // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31\n // time-hour = 2DIGIT ; 00-23\n // time-minute = 2DIGIT ; 00-59\n // time-second = 2DIGIT ; 00-58, 00-59, 00-60 (leap second)\n // time-secfrac = \".\" 1*DIGIT\n // time-numoffset = (\"+\" / \"-\") time-hour \":\" time-minute\n // time-offset = \"Z\" / time-numoffset\n // partial-time = time-hour \":\" time-minute \":\" time-second [time-secfrac]\n // full-date = date-fullyear \"-\" date-month \"-\" date-mday\n // full-time = partial-time time-offset\n // date-time = full-date \"T\" full-time\n const re = /^(\\d{4})-(\\d{2})-(\\d{2})[Tt](\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?([Zz]|[+-]\\d{2}:\\d{2})$/;\n const m = re.exec(input);\n if (!m) {\n throw new FormatSyntaxError({ input, reason: 'does not match RFC 3339 date-time grammar' });\n }\n // Construct via parseISO with the same expansion for bare Z.\n const iso = input.replace(/[tT]/, 'T').replace(/[zZ]$/, '+00:00[UTC]');\n return parseISO(iso);\n}\n\nexport function formatRFC3339(value: unknown): string {\n // RFC 3339 output is ISO 8601 with a UTC offset. toString() on a\n // ZonedDateTime produces this shape; we just verify the result looks\n // like RFC 3339 and pass it through.\n const s = formatISO(value);\n if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$/.test(s)) {\n throw new FormatSyntaxError({ input: s, reason: 'value did not produce RFC 3339-compliant output' });\n }\n return s;\n}\n\n// RFC 2822 date-time (used in email Message-ID headers, etc.). Format:\n// day-of-week, day month year hour:minute:second zone\n// e.g. \"Mon, 04 Aug 2026 15:45:30 +0000\"\n// JS Date can parse this natively, so we use Date.parse and convert\n// to a Temporal.Instant. (Date.parse's RFC 2822 support is universal\n// across engines, unlike its ISO 8601 support which varies.)\n\nexport function parseRFC2822(input: string): unknown {\n // Strict shape pre-check: Date.parse is far more permissive than RFC 2822\n // (it happily accepts ISO 8601 and other dialects), so without this gate\n // parseRFC2822(\"2026-08-04\") would succeed despite not being an RFC 2822\n // date at all. The regex mirrors RFC 2822 §3.3's date-time grammar:\n // optional day-of-week, 1-2 digit day, month name, 2-4 digit year (the\n // obsolete two-digit form included), time with optional seconds, and a\n // zone that is either a numeric ±HHMM offset or an alphabetic zone name.\n const RFC2822_SHAPE =\n /^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?\\d{1,2}\\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s+\\d{2,4}\\s+\\d{2}:\\d{2}(?::\\d{2})?\\s+(?:[+-]\\d{4}|[A-Za-z]{1,5})$/;\n if (!RFC2822_SHAPE.test(input.trim())) {\n throw new FormatSyntaxError({ input, reason: 'not a valid RFC 2822 date' });\n }\n const ms = Date.parse(input);\n if (Number.isNaN(ms)) {\n throw new FormatSyntaxError({ input, reason: 'not a valid RFC 2822 date' });\n }\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function formatRFC2822(value: unknown): string {\n // Format using format() with the right tokens, then construct via\n // the value's epoch ms. RFC 2822 requires a fixed offset, not an\n // IANA zone, so we read the value's offset directly.\n const v = value as { toEpochMilliseconds?: () => number; epochMilliseconds?: number; toInstant?: () => { epochMilliseconds: number } };\n let ms: number | undefined;\n if (typeof v?.toEpochMilliseconds === 'function') {\n ms = v.toEpochMilliseconds();\n } else if (typeof v?.epochMilliseconds === 'number') {\n // Temporal.Instant exposes epochMilliseconds as a property.\n ms = v.epochMilliseconds;\n } else if (typeof v?.toInstant === 'function') {\n // ZonedDateTime.toInstant() → Instant.\n const inst = v.toInstant();\n if (typeof (inst as { epochMilliseconds?: number }).epochMilliseconds === 'number') {\n ms = (inst as { epochMilliseconds: number }).epochMilliseconds;\n }\n }\n if (ms === undefined) {\n throw new FormatSyntaxError({ reason: `formatRFC2822() expects a value with epochMilliseconds, toEpochMilliseconds(), or toInstant(), got ${typeof value}.` });\n }\n return formatRFC2822FromMs(ms);\n}\n\nfunction formatRFC2822FromMs(ms: number): string {\n // Use Intl.DateTimeFormat with the en-US POSIX locale and the rfc822\n // format option. Modern Node ships Intl with this option; if not\n // available, fall back to manual formatting.\n try {\n const fmt = new Intl.DateTimeFormat('en-US', {\n weekday: 'short', day: '2-digit', month: 'short', year: 'numeric',\n hour: '2-digit', minute: '2-digit', second: '2-digit',\n timeZone: 'UTC', timeZoneName: 'shortOffset',\n hour12: false,\n });\n const parts = fmt.formatToParts(new Date(ms));\n /* c8 ignore start -- every part type get() is called with (weekday,\n day, month, year, hour, minute, second, timeZoneName) is always\n present given the fixed options above, so the ?? '' fallback\n can't fire without a different Intl implementation. */\n const get = (t: string) => parts.find((p) => p.type === t)?.value ?? '';\n /* c8 ignore stop */\n const tzRaw = get('timeZoneName');\n // Intl's shortOffset returns \"GMT+0\", \"GMT-8\", \"GMT+5:30\", etc.\n // RFC 2822 wants \"+0000\", \"-0800\", \"+0530\". Parse and reformat.\n const tzMatch = tzRaw?.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n let tz: string;\n if (tzMatch) {\n const sign = tzMatch[1]!;\n const hours = tzMatch[2]!.padStart(2, '0');\n const minutes = (tzMatch[3] ?? '0').padStart(2, '0');\n tz = `${sign}${hours}${minutes}`;\n /* c8 ignore start @preserve -- timeZone is hardcoded to 'UTC' above,\n which always resolves cleanly, so shortOffset always returns a\n parseable \"GMT+0\"-shaped string in this environment's ICU. This\n branch only guards against an ICU build whose shortOffset output\n doesn't match the regex; can't trigger it without mocking Intl.\n The ignore starts before the closing brace of the if-block so it\n covers the whole else-branch node c8 tracks as a single unit. */\n } else {\n tz = '+0000';\n }\n /* c8 ignore stop @preserve */\n let hour = get('hour');\n /* c8 ignore start @preserve -- hour12:false with a fixed UTC zone\n never yields \"24\" in this environment's ICU (midnight formats as\n \"00\"). Some ICU versions/locales are documented to emit \"24\" for\n hour12:false, so this stays as a defensive normalization; not\n reachable here without mocking Intl.DateTimeFormat's output. */\n if (hour === '24') hour = '00';\n /* c8 ignore stop @preserve */\n return `${get('weekday')}, ${get('day')} ${get('month')} ${get('year')} ${hour}:${get('minute')}:${get('second')} ${tz}`;\n /* c8 ignore start @preserve -- Intl.DateTimeFormat is constructed\n above with a literal, always-valid timeZone: 'UTC' and no other\n user input reaches the constructor or formatToParts call, so this\n catch can't be triggered through the public API in a normal Intl\n environment (e.g. Node with full-icu). Kept as a genuine fallback\n for runtimes with a broken or absent Intl implementation. */\n } catch {\n // Fallback: hand-rolled RFC 2822 format via Date's UTC methods.\n const d = new Date(ms);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n const wd = days[d.getUTCDay()];\n const dd = String(d.getUTCDate()).padStart(2, '0');\n const mon = months[d.getUTCMonth()]!;\n const yyyy = d.getUTCFullYear();\n const hh = String(d.getUTCHours()).padStart(2, '0');\n const mm = String(d.getUTCMinutes()).padStart(2, '0');\n const ss = String(d.getUTCSeconds()).padStart(2, '0');\n return `${wd}, ${dd} ${mon} ${yyyy} ${hh}:${mm}:${ss} +0000`;\n }\n /* c8 ignore stop @preserve */\n}\n\n// RFC 7231 defines three formats: IMF-fixdate, RFC 850, asctime.\n// The canonical format is IMF-fixdate (RFC 2822-like with required\n// seconds and UTC-only). Delegates to Date.parse for parsing and to\n// formatRFC2822 for formatting (they're nearly identical).\n\nexport function parseHTTPDate(input: string): unknown {\n const trimmed = input.trim();\n const ms = Date.parse(trimmed);\n if (Number.isNaN(ms)) {\n throw new FormatSyntaxError({ input, reason: 'not a valid HTTP-date' });\n }\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function formatHTTPDate(value: unknown): string {\n // IMF-fixdate format: \"Sun, 06 Nov 1994 08:49:37 GMT\"\n const instant = (value as { toInstant?: () => { epochMilliseconds: number } })?.toInstant?.()\n ?? (value as { epochMilliseconds?: number });\n const ms = typeof instant === 'object' && instant !== null && 'epochMilliseconds' in instant\n ? (instant as { epochMilliseconds: number }).epochMilliseconds\n : (() => { throw new FormatSyntaxError({ reason: `formatHTTPDate() expects an Instant or ZonedDateTime, got ${typeof value}.` }); })();\n const d = new Date(ms);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n const wd = days[d.getUTCDay()];\n const dd = String(d.getUTCDate()).padStart(2, '0');\n const mon = months[d.getUTCMonth()]!;\n const yyyy = d.getUTCFullYear();\n const hh = String(d.getUTCHours()).padStart(2, '0');\n const mm = String(d.getUTCMinutes()).padStart(2, '0');\n const ss = String(d.getUTCSeconds()).padStart(2, '0');\n return `${wd}, ${dd} ${mon} ${yyyy} ${hh}:${mm}:${ss} GMT`;\n}\n\n// All epoch conversions go through Temporal.Instant, which has spec\n// methods for each epoch granularity. Wrapped here for the\n// temporal-fmt surface so callers don't have to import Temporal\n// directly.\n\nfunction getInstant(value: unknown): { epochMilliseconds: number; epochNanoseconds: bigint } {\n const v = value as { toInstant?: () => { epochMilliseconds: number; epochNanoseconds: bigint }; epochMilliseconds?: number; epochNanoseconds?: bigint };\n if (typeof v?.toInstant === 'function') {\n const inst = v.toInstant();\n // Prefer the instant's own epochNanoseconds when present — same\n // precision reasoning as below, since toInstant() can return a\n // real Temporal.Instant carrying both fields.\n if (typeof (inst as { epochNanoseconds?: bigint }).epochNanoseconds === 'bigint') {\n return { epochMilliseconds: inst.epochMilliseconds, epochNanoseconds: (inst as { epochNanoseconds: bigint }).epochNanoseconds };\n }\n return { epochMilliseconds: inst.epochMilliseconds, epochNanoseconds: BigInt(inst.epochMilliseconds) * 1_000_000n };\n }\n // Check epochNanoseconds before epochMilliseconds: a real\n // Temporal.Instant exposes both, and epochMilliseconds is truncated\n // to millisecond precision. Deriving epochNanoseconds by scaling\n // epochMilliseconds back up would silently drop sub-ms precision\n // that was available on the object all along.\n if (typeof v?.epochNanoseconds === 'bigint') {\n return { epochMilliseconds: Number(v.epochNanoseconds / 1_000_000n), epochNanoseconds: v.epochNanoseconds };\n }\n if (typeof v?.epochMilliseconds === 'number') {\n return { epochMilliseconds: v.epochMilliseconds, epochNanoseconds: BigInt(v.epochMilliseconds) * 1_000_000n };\n }\n throw new FormatSyntaxError({ reason: `expected an Instant or ZonedDateTime, got ${typeof value}.` });\n}\n\nfunction requireInstant(): NonNullable<TemporalNamespace['Instant']> {\n const t = getTemporal().Instant;\n if (!t) throw new Error('temporal-fmt: Temporal.Instant is not available in this implementation.');\n return t;\n}\n\nexport function toUnixSeconds(value: unknown): number {\n return getInstant(value).epochMilliseconds / 1000;\n}\n\nexport function fromUnixSeconds(seconds: number): unknown {\n return requireInstant().fromEpochMilliseconds(seconds * 1000);\n}\n\nexport function fromUnixMilliseconds(ms: number): unknown {\n return requireInstant().fromEpochMilliseconds(ms);\n}\n\nexport function fromUnixMicroseconds(µs: number): unknown {\n // Temporal.Instant has no fromEpochMicroseconds in the spec — only\n // fromEpochMilliseconds and fromEpochNanoseconds. Convert via ns.\n return requireInstant().fromEpochNanoseconds(BigInt(µs) * 1_000n);\n}\n\nexport function fromUnixNanoseconds(ns: bigint): unknown {\n return requireInstant().fromEpochNanoseconds(ns);\n}\n\nexport function toUnixMilliseconds(value: unknown): number {\n return getInstant(value).epochMilliseconds;\n}\n\nexport function toUnixMicroseconds(value: unknown): number {\n const { epochNanoseconds } = getInstant(value);\n return Number(epochNanoseconds / 1_000n);\n}\n\nexport function toUnixNanoseconds(value: unknown): bigint {\n return getInstant(value).epochNanoseconds;\n}\n\n// SQL-oriented helpers. SQL date/time formats are simpler than the\n// network standards above — just 'YYYY-MM-DD' for dates, 'HH:MM:SS'\n// for times, 'YYYY-MM-DD HH:MM:SS' for datetimes. Delegating to\n// parse() with the right format strings keeps the validation surface\n// consistent.\nexport function parseSQL(input: string): unknown {\n const trimmed = input.trim();\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n return parse('yyyy-MM-dd', trimmed);\n }\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return parse('yyyy-MM-dd HH:mm:ss', trimmed);\n }\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(trimmed)) {\n return parseISO(trimmed);\n }\n throw new FormatSyntaxError({ input, reason: 'not a recognized SQL date/time format' });\n}\n\nexport function formatSQL(value: unknown): string {\n // Detect what kind of Temporal value this is and pick the format.\n const v = value as { year?: number; hour?: number };\n if (typeof v?.hour === 'number') {\n return format(value as Parameters<typeof format>[0], 'yyyy-MM-dd HH:mm:ss');\n }\n return format(value as Parameters<typeof format>[0], 'yyyy-MM-dd');\n}","// Duration extensions. The existing formatDuration()\n// in formatDuration.ts stays as-is; this module adds the rest of the\n// duration surface: parseDuration, parseISODuration, formatISODuration,\n// formatDurationToParts, balanceDuration, roundDuration (re-exported\n// from rounding.ts), totalDuration, compareDuration, addDuration,\n// subtractDuration.\n//\n// All operate on plain field bags ({ years, months, weeks, days, hours,\n// minutes, seconds, milliseconds, microseconds, nanoseconds }) rather\n// than Temporal.Duration instances — same convention as the rest of\n// this module set, so callers can use these without committing to a\n// Temporal implementation. addDuration/subtractDuration produce a new\n// field bag; totalDuration produces a number; compareDuration produces\n// -1/0/1.\n\nimport { roundDuration, fieldToNs, type DurationFields } from './rounding.js';\nimport { formatDuration, renderDurationPieces } from './formatDuration.js';\nimport { InvalidDurationError, FormatSyntaxError } from './errors.js';\n\n// Re-export roundDuration here so callers can import everything duration-\n// related from one place.\nexport { roundDuration };\nexport type { DurationFields };\n\n// Mirrors formatToParts in format.ts. Returns the same piece list\n// formatDuration() would consume, but split into parts instead of\n// joined. Useful for callers building custom UIs (one DOM node per\n// unit) or doing their own unit suppression logic.\n\nimport type { Piece } from './tokenize.js';\n\nexport interface DurationPart {\n type: 'literal' | 'token';\n value: string;\n token?: string;\n}\n\n// Delegates to formatDuration()'s own piece renderer, so the joined\n// string and the parts can never disagree. (This used to re-tokenize the\n// format string and re-derive part boundaries by searching the rendered\n// string with indexOf — which mis-sliced whenever a literal's text also\n// occurred inside an earlier token's rendered value, e.g. a \"s\"\n// separator against \"5 seconds\".) Zero-suppressed tokens emit an empty\n// token part and their neighboring literals are kept, matching the\n// documented formatDurationToParts contract.\nexport function formatDurationToParts(\n duration: Record<string, unknown>,\n formatStr: string,\n options: Parameters<typeof formatDuration>[2] = {},\n): DurationPart[] {\n return renderDurationPieces(duration, formatStr, options).map((p) =>\n p.kind === 'token' ? { type: 'token' as const, value: p.value, token: p.token } : { type: 'literal' as const, value: p.value },\n );\n}\n\n// ISO 8601 duration format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S\n// e.g. \"P3Y6M4DT12H30M5S\", \"PT1H30M\", \"P1W\", \"P0D\".\n// Parsed into a DurationFields bag.\nexport function parseISODuration(input: string): DurationFields {\n // Loose grammar: P [years Y] [months M] [weeks W] [days D] [T [hours H] [minutes M] [seconds S]]\n // Weeks is an ISO 8601-2 extension; widely supported.\n const re = /^P(?:(?:(\\d+(?:\\.\\d+)?)Y)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)W)?(?:(\\d+(?:\\.\\d+)?)D)?)?(?:T(?:(\\d+(?:\\.\\d+)?)H)?(?:(\\d+(?:\\.\\d+)?)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/;\n const m = re.exec(input);\n if (!m) {\n throw new InvalidDurationError({ input, reason: 'does not match ISO 8601 duration grammar (P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S)' });\n }\n // All-zero / empty: P0D is valid ISO for zero duration.\n if (m.slice(1).every((g) => g === undefined)) {\n throw new InvalidDurationError({ input, reason: 'duration has no fields (use \"P0D\" for zero duration)' });\n }\n const toNum = (s: string | undefined): number => s === undefined ? 0 : Number(s);\n return {\n years: toNum(m[1]),\n months: toNum(m[2]),\n weeks: toNum(m[3]),\n days: toNum(m[4]),\n hours: toNum(m[5]),\n minutes: toNum(m[6]),\n seconds: toNum(m[7]),\n };\n}\n\nexport function formatISODuration(duration: DurationFields): string {\n const parts: string[] = ['P'];\n const years = duration.years ?? 0;\n const months = duration.months ?? 0;\n const weeks = duration.weeks ?? 0;\n const days = duration.days ?? 0;\n const hours = duration.hours ?? 0;\n const minutes = duration.minutes ?? 0;\n const seconds = duration.seconds ?? 0;\n if (years) parts.push(`${years}Y`);\n if (months) parts.push(`${months}M`);\n if (weeks) parts.push(`${weeks}W`);\n if (days) parts.push(`${days}D`);\n if (hours || minutes || seconds) {\n parts.push('T');\n if (hours) parts.push(`${hours}H`);\n if (minutes) parts.push(`${minutes}M`);\n if (seconds) parts.push(`${seconds}S`);\n }\n // parts is just ['P'] when every field is zero -- ISO has no empty\n // duration, so fall back to P0D.\n if (parts.length === 1) parts.push('0D');\n return parts.join('');\n}\n\n// Minimal tokenizer for the duration format string. Reuses the same\n// quoted-literal and greedy-match logic as tokenize.ts.\nfunction tokenizeDurationFormat(formatStr: string): Array<Piece & { unit?: string }> {\n const DURATION_TOKEN_STRINGS = ['y', 'o', 'w', 'd', 'h', 'm', 's', 'S']\n .flatMap((single) => [single + single + single, single + single, single])\n .sort((a, b) => b.length - a.length);\n const pieces: Array<Piece & { unit?: string }> = [];\n let i = 0;\n while (i < formatStr.length) {\n const ch = formatStr[i];\n if (ch === \"'\") {\n if (formatStr[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < formatStr.length) {\n if (formatStr[j] === \"'\") {\n if (formatStr[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += formatStr[j]!;\n j += 1;\n }\n if (!closed) throw new FormatSyntaxError({ format: formatStr, reason: 'unterminated quote' });\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = DURATION_TOKEN_STRINGS.find((tok) => formatStr.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match, unit: match[0] });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n}\n\nfunction appendLiteral(pieces: Array<Piece & { unit?: string }>, 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\nfunction findNextLiteralStart(\n tokens: Array<Piece & { unit?: string }>,\n _currentTok: Piece & { unit?: string },\n full: string,\n pos: number,\n): number {\n // Find the position in `full` where the next literal after the\n // current token starts. For now, just return the end of `full` —\n // formatDurationToParts is best-effort for the common case where\n // tokens are separated by literals, and degrades gracefully when\n // they aren't.\n // A more precise implementation would walk the tokens array from\n // the current position and use the next literal piece's text to\n // find the boundary in `full`. Doing it correctly requires handling\n // zero-value unit suppression, which is why this helper exists.\n // For now, we use the next literal piece's value as a search anchor.\n const idx = tokens.indexOf(_currentTok);\n for (let j = idx + 1; j < tokens.length; j++) {\n const t = tokens[j]!;\n if (t.kind === 'literal') {\n const found = full.indexOf(t.value, pos);\n if (found >= 0) return found;\n }\n }\n return full.length;\n}\n\n// ISO 8601 duration format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S\n// Parses a duration-format string (the tokenized format formatDuration\n// accepts) into a DurationFields bag. Inverse of formatDuration().\nexport function parseDuration(input: string, formatStr: string, options: { locale?: string } = {}): DurationFields {\n // Build a capturing regex from the format string. Each token\n // captures a number; literals are matched literally.\n const tokens = tokenizeDurationFormat(formatStr);\n const fields: DurationFields = {};\n const groupSpecs: Array<{ unit: keyof DurationFields; form: 'numeric' | 'short' | 'long' }> = [];\n let regex = '';\n for (const tok of tokens) {\n if (tok.kind === 'literal') {\n regex += tok.value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n } else {\n const unit = tok.unit!;\n const form = tok.value.length === 1 ? 'numeric' : tok.value.length === 2 ? 'short' : 'long';\n // Map token letter to DurationFields key.\n /* c8 ignore start @preserve -- the final `: null` branch, and the\n `if (key === null)` throw it feeds, are both unreachable: tok.unit\n is always one of the 8 letters in DURATION_TOKEN_STRINGS (the same\n source set this ternary checks against), so `null` can't be\n produced by any token the tokenizer emits. Kept as a guard against\n a future letter being added to one set without the other. */\n const key: keyof DurationFields | null = (\n unit === 'y' ? 'years'\n : unit === 'o' ? 'months'\n : unit === 'w' ? 'weeks'\n : unit === 'd' ? 'days'\n : unit === 'h' ? 'hours'\n : unit === 'm' ? 'minutes'\n : unit === 's' ? 'seconds'\n : unit === 'S' ? 'milliseconds'\n : null\n );\n if (key === null) {\n throw new FormatSyntaxError({ format: formatStr, token: tok.value, reason: 'unknown duration token' });\n }\n /* c8 ignore stop @preserve */\n // Numeric form: capture digits (and optional sign).\n // Short/long form: capture digits followed by optional unit text.\n // We'll handle unit-text stripping after the match.\n if (form === 'numeric') {\n regex += '(-?\\\\d+)';\n } else {\n // Short form like \"2y\" or \"2yrs\"; long form like \"2 years\" or \"2 year\".\n // Capture the number; unit text is matched but not captured.\n regex += '(\\\\d+)\\\\s*\\\\w*';\n }\n groupSpecs.push({ unit: key, form });\n }\n }\n const re = new RegExp(`^${regex}$`, 'u');\n const m = re.exec(input);\n if (!m) {\n throw new FormatSyntaxError({ input, format: formatStr, reason: 'does not match duration format' });\n }\n groupSpecs.forEach((spec, i) => {\n const raw = m[i + 1]!;\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n throw new InvalidDurationError({ input, reason: `token for ${spec.unit} matched \"${raw}\" but isn't a finite number` });\n }\n fields[spec.unit] = value;\n });\n return fields;\n}\n\n// Balances a duration's absolute fields (days/hours/minutes/seconds/ms/µs/ns)\n// so each carries its natural range and excess carries into the next\n// larger unit. Doesn't touch calendar-bound fields (years/months/weeks)\n// since those need a relativeTo to balance correctly.\nexport function balanceDuration(duration: DurationFields): DurationFields {\n const NS_PER: Array<[keyof DurationFields, bigint]> = [\n ['days', 86_400n * 1_000_000_000n],\n ['hours', 3_600n * 1_000_000_000n],\n ['minutes', 60n * 1_000_000_000n],\n ['seconds', 1_000_000_000n],\n ['milliseconds', 1_000_000n],\n ['microseconds', 1_000n],\n ['nanoseconds', 1n],\n ];\n // Sum everything from days down to ns.\n let totalNs = 0n;\n for (const [k, nsPer] of NS_PER) {\n const v = duration[k];\n if (typeof v === 'number') totalNs += fieldToNs(v, nsPer, k);\n }\n // Re-distribute.\n const result: DurationFields = { ...duration };\n let remaining = totalNs;\n for (const [k, nsPer] of NS_PER) {\n const count = remaining / nsPer;\n remaining -= count * nsPer;\n result[k] = Number(count);\n }\n return result;\n}\n\n// Sums all absolute fields into a single number expressed in the\n// requested unit. Throws for calendar-bound target units (years/months/\n// weeks) since those need a relativeTo.\nexport function totalDuration(duration: DurationFields, unit: 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds' | 'microseconds' | 'nanoseconds'): number {\n const NS_PER: Record<string, bigint> = {\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n };\n let totalNs = 0n;\n for (const k of Object.keys(NS_PER) as Array<keyof typeof NS_PER>) {\n const v = duration[k as keyof DurationFields];\n if (typeof v === 'number') totalNs += fieldToNs(v, NS_PER[k]!, k);\n }\n const divisor = NS_PER[unit];\n if (!divisor) {\n throw new InvalidDurationError({ reason: `totalDuration() does not support unit \"${unit}\" (calendar-bound units need a relativeTo)` });\n }\n return Number(totalNs) / Number(divisor);\n}\n\n// Compares two durations by their total absolute length. Returns -1/0/1.\nexport function compareDuration(a: DurationFields, b: DurationFields): number {\n const aNs = totalDurationNs(a);\n const bNs = totalDurationNs(b);\n if (aNs < bNs) return -1;\n if (aNs > bNs) return 1;\n return 0;\n}\n\nfunction totalDurationNs(d: DurationFields): bigint {\n const NS_PER: Record<string, bigint> = {\n days: 86_400n * 1_000_000_000n,\n hours: 3_600n * 1_000_000_000n,\n minutes: 60n * 1_000_000_000n,\n seconds: 1_000_000_000n,\n milliseconds: 1_000_000n,\n microseconds: 1_000n,\n nanoseconds: 1n,\n };\n let total = 0n;\n for (const k of Object.keys(NS_PER)) {\n const v = d[k as keyof DurationFields];\n if (typeof v === 'number') total += fieldToNs(v, NS_PER[k]!, k);\n }\n return total;\n}\n\n// Adds two durations field-by-field. Doesn't balance — call\n// balanceDuration() afterwards if you want a balanced result.\nexport function addDuration(a: DurationFields, b: DurationFields): DurationFields {\n const keys: Array<keyof DurationFields> = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];\n const result: DurationFields = {};\n for (const k of keys) {\n result[k] = (a[k] ?? 0) + (b[k] ?? 0);\n }\n return result;\n}\n\nexport function subtractDuration(a: DurationFields, b: DurationFields): DurationFields {\n return addDuration(a, negateDuration(b));\n}\n\nfunction negateDuration(d: DurationFields): DurationFields {\n const result: DurationFields = {};\n for (const k of Object.keys(d) as Array<keyof DurationFields>) {\n result[k] = -(d[k] ?? 0);\n }\n return result;\n}\n","// Relative time helpers. The existing formatDistance() in\n// formatDistance.ts stays; this module adds formatRelative() and\n// formatRelativeToNow().\n//\n// Semantic distinction:\n// formatDistance(date1, date2) — \"3 days ago\" / \"in 2 hours\"\n// Describes the gap between two values, delegating unit selection\n// and pluralization to Intl.RelativeTimeFormat.\n// formatRelative(date, baseDate) — \"yesterday\" / \"tomorrow\" / \"today\"\n// / \"in 3 days\" / \"last week\"\n// Calendar-relative: uses the calendar date diff, not the ms diff.\n// \"in 1 day\" means \"tomorrow\", even if it's 23 hours away.\n// formatRelativeToNow(date) — same as formatRelative(date, now)\n\nimport { differenceInDays } from './arithmetic.js';\nimport { DEFAULT_LOCALE, type FormatOptions } from './tokens.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface FormatRelativeOptions extends FormatOptions {\n // 'auto' (default) lets Intl.RelativeTimeFormat use natural forms like\n // \"yesterday\"/\"tomorrow\"/\"now\" when the value lands on ±1 or 0.\n // 'always' forces the strict \"1 day ago\"/\"in 1 day\" form.\n numeric?: 'always' | 'auto';\n}\n\nconst rtfCache = new Map<string, Intl.RelativeTimeFormat>();\nconst MAX_RTF_CACHE_SIZE = 100;\n\nfunction getRtf(locale: string, numeric: 'always' | 'auto'): Intl.RelativeTimeFormat {\n // Same cache shape as formatDistance.ts uses.\n const key = `${locale}|${numeric}`;\n let rtf = rtfCache.get(key);\n if (rtf) return rtf;\n if (rtfCache.size >= MAX_RTF_CACHE_SIZE) {\n const oldestKey = rtfCache.keys().next().value;\n if (oldestKey !== undefined) rtfCache.delete(oldestKey);\n }\n try {\n rtf = new Intl.RelativeTimeFormat(normalizeLocaleTag(locale), { numeric });\n } catch (err) {\n // Malformed locale tags reach Intl as a bare RangeError; surface the\n // library's typed error instead. (Every failure mode of these\n // constructors with a string locale is a RangeError, so converting\n // unconditionally loses nothing — the original message is preserved\n // in `reason` either way.)\n throw new InvalidLocaleError({ actual: locale, reason: (err as Error).message });\n }\n rtfCache.set(key, rtf);\n return rtf;\n}\n\n// Calendar-relative \"describe date1 as if standing at date2\" — uses\n// differenceInDays (which counts date boundaries, not 24-hour periods),\n// so \"2026-08-04 23:59\" relative to \"2026-08-05 00:00\" reads as\n// \"yesterday\" (1 day ago), not \"1 second ago\".\n//\n// differenceInDays(a, b) returns b - a; for formatRelative(date1, date2)\n// we want date1 - date2 (so positive means date1 is in the future relative\n// to date2). Swap the argument order.\nexport function formatRelative(\n date1: unknown,\n date2: unknown,\n options: FormatRelativeOptions = {},\n): string {\n const locale = options.locale ?? DEFAULT_LOCALE;\n const numeric = options.numeric ?? 'auto';\n const rtf = getRtf(locale, numeric);\n const dayDiff = -differenceInDays(date1, date2);\n const absDays = Math.abs(dayDiff);\n\n if (absDays === 0) {\n return rtf.format(0, 'day'); // \"now\" / \"in 0 days\"\n }\n if (absDays < 7) {\n return rtf.format(dayDiff, 'day');\n }\n if (absDays < 30) {\n return rtf.format(-Math.trunc(-dayDiff / 7), 'week');\n }\n if (absDays < 365) {\n return rtf.format(-Math.trunc(-dayDiff / 30), 'month');\n }\n return rtf.format(-Math.trunc(-dayDiff / 365), 'year');\n}\n\nexport function formatRelativeToNow(\n date: unknown,\n options: FormatRelativeOptions = {},\n): string {\n // Use the system's current date as the reference. PlainDate.from a\n // JS Date so differenceInDays sees the right shape.\n const now = new Date();\n const nowFields = {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n };\n return formatRelative(date, nowFields, options);\n}\n","// Locale registration and lookup. Extends the\n// existing registerLocaleVocab() surface in localeVocab.ts with\n// registerLocale() / getLocale() / hasLocale() and a richer LocaleVocab\n// that covers quarters, eras, ordinals, duration units, and relative-\n// time language.\n//\n// The existing localeVocab.ts stays as the implementation for months/\n// weekdays/day-periods (those are what tokens.ts's MMMM/MMM/EEEE/EEE/a\n// tokens read). This module wraps it and adds the extended fields.\n\nimport { registerLocaleVocab, getLocaleVocab, type LocaleVocab as BaseLocaleVocab } from './localeVocab.js';\nimport { InvalidLocaleError } from './errors.js';\n\nexport interface ExtendedLocaleVocab extends BaseLocaleVocab {\n // Quarters: Q1..Q4 long form (\"First quarter\", etc.). Default falls\n // back to \"Q1\"..\"Q4\" if not registered.\n quartersLong?: string[];\n quartersShort?: string[];\n // Eras: [\"BC\", \"AD\"] for gregory, etc. Default [\"BC\", \"AD\"].\n erasLong?: string[];\n erasShort?: string[];\n // Ordinal suffix rules: [\"st\", \"nd\", \"rd\", \"th\"] for English. Default\n // empty (the existing 'do' token has English-only ordinal logic baked\n // into tokens.ts; this field lets a custom locale override it).\n ordinals?: string[];\n // Duration unit names: long singular/plural for each unit. Used by\n // formatDuration's locale-aware path when Intl.NumberFormat isn't\n // desired (e.g. for a made-up locale Intl doesn't know about).\n durationUnits?: {\n years: [string, string];\n months: [string, string];\n weeks: [string, string];\n days: [string, string];\n hours: [string, string];\n minutes: [string, string];\n seconds: [string, string];\n milliseconds: [string, string];\n };\n // Relative-time language: words for \"ago\" / \"in\" / \"now\". Used by\n // formatRelative()'s locale-aware path when Intl.RelativeTimeFormat\n // isn't available or doesn't cover a registered locale.\n relativeTime?: {\n past: string;\n future: string;\n now: string;\n };\n}\n\n// Bounded indirectly: every entry here is added by registerLocale(), which\n// first passes registerLocaleVocab() — and THAT registry caps new locales\n// at 500 (MAX_CUSTOM_VOCABS, RangeError past it). So extendedVocabs can\n// never exceed 500 keys; no separate cap is needed (and an unreachable one\n// would only add dead branches).\nconst extendedVocabs = new Map<string, ExtendedLocaleVocab>();\nconst MAX_LOCALE_TAG_LENGTH = 256;\nconst MAX_VOCAB_STRING_LENGTH = 256;\n\nfunction canonicalKey(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, '-')).toString().toLowerCase();\n } catch {\n return locale;\n }\n}\n\nexport function registerLocale(locale: string, vocab: ExtendedLocaleVocab): void {\n if (typeof locale !== 'string' || locale.length === 0) {\n throw new InvalidLocaleError({ actual: String(locale), reason: 'locale string must be non-empty' });\n }\n if (locale.length > MAX_LOCALE_TAG_LENGTH) {\n throw new InvalidLocaleError({ actual: String(locale.length), reason: `locale tag must be at most ${MAX_LOCALE_TAG_LENGTH} characters` });\n }\n // Validate the base vocab fields (months/weekdays/day-periods) via\n // the existing registerLocaleVocab — it has the strict-shape checks.\n registerLocaleVocab(locale, vocab);\n // Extended fields are optional; only validate the ones that are present.\n validateExtended(vocab, locale);\n const key = canonicalKey(locale);\n extendedVocabs.set(key, { ...vocab });\n}\n\nfunction validateExtended(vocab: ExtendedLocaleVocab, locale: string): void {\n const checkArray = (val: unknown, key: string, length: number) => {\n if (val === undefined) return;\n if (!Array.isArray(val)) {\n throw new InvalidLocaleError({ actual: String(val), reason: `locale \"${locale}\": \"${key}\" must be an array` });\n }\n if (val.length !== length) {\n throw new InvalidLocaleError({ actual: String(val.length), reason: `locale \"${locale}\": \"${key}\" must have ${length} entries (got ${val.length})` });\n }\n for (let i = 0; i < val.length; i++) {\n if (typeof val[i] !== 'string' || val[i]!.length === 0) {\n throw new InvalidLocaleError({ actual: String(val[i]), reason: `locale \"${locale}\": \"${key}[${i}]\" must be a non-empty string` });\n }\n if (val[i]!.length > MAX_VOCAB_STRING_LENGTH) {\n throw new InvalidLocaleError({ actual: String(val[i]!.length), reason: `locale \"${locale}\": \"${key}[${i}]\" must be at most ${MAX_VOCAB_STRING_LENGTH} characters` });\n }\n }\n };\n checkArray(vocab.quartersLong, 'quartersLong', 4);\n checkArray(vocab.quartersShort, 'quartersShort', 4);\n checkArray(vocab.erasLong, 'erasLong', 2);\n checkArray(vocab.erasShort, 'erasShort', 2);\n checkArray(vocab.ordinals, 'ordinals', 4);\n}\n\nexport function getLocale(locale: string): ExtendedLocaleVocab | undefined {\n // Returns the extended vocab for a locale, OR undefined (falls back\n // to Intl). Combines the base vocab (from localeVocab.ts, which\n // includes Intl defaults) with any extended fields registered here.\n const key = canonicalKey(locale);\n const ext = extendedVocabs.get(key);\n if (ext) return ext;\n // Fall back to base vocab (Intl-derived) without extended fields.\n try {\n const base = getLocaleVocab(locale);\n return { ...base };\n } catch {\n return undefined;\n }\n}\n\nexport function hasLocale(locale: string): boolean {\n const key = canonicalKey(locale);\n return extendedVocabs.has(key);\n}\n\n// Re-export the base vocab types for callers that want them.\nexport type { LocaleVocab } from './localeVocab.js';\n","// Central configuration. Per-call options are the\n// default convention throughout this library (see formatDistance's\n// cutoffs precedent — explicitly NOT a global). This module provides\n// a typed ConfigOptions surface that callers can build once and pass\n// to any temporal-fmt function, plus a set of defaults that mirror\n// the existing per-call defaults.\n//\n// No global mutable state. The plan is explicit: \"No mysterious\n// mutable global state — this is a hard constraint, not a preference.\"\n// This module honors that: createConfig() returns an immutable config\n// object; there is no setConfig().\n\nimport { DEFAULT_LOCALE } from './tokens.js';\nimport type { NumberingSystem } from './numbering.js';\n\nexport interface TemporalFmtConfig {\n // BCP-47 locale tag. Default 'en-US'.\n locale: string;\n // Calendar identifier ('gregory', 'hebrew', etc.) or undefined to\n // use the locale's default.\n calendar?: string;\n // IANA timezone id ('America/New_York', etc.) or undefined to use\n // the system timezone.\n timezone?: string;\n // Numbering system. Default 'latn'.\n numberingSystem: NumberingSystem | string;\n // ISO week rules: 1 = Monday, 7 = Sunday. Default 1 (matches the\n // existing isoWeek.ts convention).\n firstDayOfWeek: 1 | 7;\n // Rounding mode for round()/roundDuration(). Default 'nearest'.\n roundingMode: 'nearest' | 'floor' | 'ceil' | 'trunc';\n // Disambiguation for ZonedDateTime construction during gaps/overlaps.\n // Default 'compatible' (Temporal's default).\n disambiguation: 'compatible' | 'earlier' | 'later' | 'reject';\n // Overflow mode for field construction (Feb 30 etc.).\n // Default 'reject' (matches parse()'s existing behavior).\n overflow: 'constrain' | 'reject';\n // Parse leniency. Default false (strict).\n parseLenient: boolean;\n // Duration zero-value display. Default false (omit zeros).\n durationShowZeroValues: boolean;\n}\n\nexport const DEFAULT_CONFIG: TemporalFmtConfig = {\n locale: DEFAULT_LOCALE,\n numberingSystem: 'latn',\n firstDayOfWeek: 1,\n roundingMode: 'nearest',\n disambiguation: 'compatible',\n overflow: 'reject',\n parseLenient: false,\n durationShowZeroValues: false,\n};\n\n// Creates a config object with the caller's overrides merged onto\n// the defaults. Returns a frozen object so callers can't mutate it\n// after the fact — same immutability convention as Temporal's own\n// options.\nexport function createConfig(overrides: Partial<TemporalFmtConfig> = {}): Readonly<TemporalFmtConfig> {\n const merged: TemporalFmtConfig = { ...DEFAULT_CONFIG, ...overrides };\n // Validate.\n if (typeof merged.locale !== 'string' || merged.locale.length === 0) {\n throw new Error(`temporal-fmt: config.locale must be a non-empty string (got ${String(merged.locale)}).`);\n }\n if (merged.firstDayOfWeek !== 1 && merged.firstDayOfWeek !== 7) {\n throw new Error(`temporal-fmt: config.firstDayOfWeek must be 1 (Monday) or 7 (Sunday) (got ${merged.firstDayOfWeek}).`);\n }\n if (!['nearest', 'floor', 'ceil', 'trunc'].includes(merged.roundingMode)) {\n throw new Error(`temporal-fmt: config.roundingMode \"${merged.roundingMode}\" is not recognized.`);\n }\n if (!['compatible', 'earlier', 'later', 'reject'].includes(merged.disambiguation)) {\n throw new Error(`temporal-fmt: config.disambiguation \"${merged.disambiguation}\" is not recognized.`);\n }\n if (!['constrain', 'reject'].includes(merged.overflow)) {\n throw new Error(`temporal-fmt: config.overflow \"${merged.overflow}\" is not recognized.`);\n }\n return Object.freeze(merged);\n}\n\n// Merges a per-call options object with a config. Per-call options take\n// precedence. Used by format()/parse()/etc. to fold a caller's config\n// into the per-call options they pass.\nexport function mergeWithConfig<T extends Record<string, unknown>>(\n config: Readonly<TemporalFmtConfig> | undefined,\n perCall: T,\n): T & { locale?: string; calendar?: string; timezone?: string; numberingSystem?: string; lenient?: boolean } {\n if (!config) return perCall;\n const result = { ...perCall } as T & { locale?: string; calendar?: string; timezone?: string; numberingSystem?: string; lenient?: boolean };\n // Only fill in fields the per-call options don't specify.\n if (result.locale === undefined) result.locale = config.locale;\n if (result.calendar === undefined && config.calendar !== undefined) result.calendar = config.calendar;\n if (result.timezone === undefined && config.timezone !== undefined) result.timezone = config.timezone;\n if (result.numberingSystem === undefined) result.numberingSystem = config.numberingSystem;\n if (result.lenient === undefined && config.parseLenient) result.lenient = true;\n return result;\n}","// Intervals / ranges. An interval is a pair of\n// (start, end) Temporal values with bounds semantics: open, closed,\n// or half-open. Operations: contains, overlaps, intersects, isBefore,\n// isAfter, intersection, union, difference, subtract, mergeIntervals,\n// splitInterval. Plus formatRange / formatRangeToParts with intelligent\n// collapsing.\n//\n// All operations are field-based (no instanceof), matching the rest of\n// the library. Internally uses the comparison helpers from comparison.ts\n// to order endpoints.\n\nimport { compare } from './comparison.js';\nimport { format, formatToParts, type FormattedPart } from './format.js';\nimport { asDateFieldView, type DateFieldView } from './calendarUtils.js';\nimport { normalizeLocaleTag } from './localeVocab.js';\nimport type { FormatOptions } from './tokens.js';\n\nexport type IntervalBounds = 'open' | 'closed' | 'half-open-start' | 'half-open-end';\n\nexport interface Interval {\n start: unknown;\n end: unknown;\n bounds: IntervalBounds;\n}\n\nexport function interval(start: unknown, end: unknown, bounds: IntervalBounds = 'closed'): Interval {\n // Validate that start ≤ end, otherwise the interval is malformed.\n const cmp = compare(start, end);\n if (cmp > 0) {\n throw new Error(\n `temporal-fmt: interval start must be ≤ end (got start > end). Pass them in chronological order.`\n );\n }\n return { start, end, bounds };\n}\n\n// Inclusive containment check accounting for bounds semantics.\n// - 'closed': [start, end] — both endpoints included\n// - 'open': (start, end) — neither endpoint included\n// - 'half-open-start': (start, end] — start excluded, end included\n// - 'half-open-end': [start, end) — start included, end excluded\nexport function contains(iv: Interval, value: unknown): boolean {\n const startCmp = compare(value, iv.start);\n const endCmp = compare(value, iv.end);\n const afterStart = iv.bounds === 'open' || iv.bounds === 'half-open-end'\n ? startCmp > 0\n : startCmp >= 0;\n const beforeEnd = iv.bounds === 'open' || iv.bounds === 'half-open-start'\n ? endCmp < 0\n : endCmp <= 0;\n return afterStart && beforeEnd;\n}\n\n// Two intervals overlap iff their ranges intersect, regardless of bounds.\n// intersects() is the boolean form; intersection() returns the actual\n// overlap interval.\nexport function overlaps(a: Interval, b: Interval): boolean {\n return intersects(a, b);\n}\n\nexport function intersects(a: Interval, b: Interval): boolean {\n // a is entirely before b: a.end < b.start\n if (compare(a.end, b.start) < 0) return false;\n // a is entirely after b: a.start > b.end\n if (compare(a.start, b.end) > 0) return false;\n // Touching endpoints with both-closed bounds counts as overlap;\n // open bounds would not. To keep it simple, touching counts.\n return true;\n}\n\nexport function isBefore(a: Interval, b: Interval): boolean {\n return compare(a.end, b.start) < 0;\n}\n\nexport function isAfter(a: Interval, b: Interval): boolean {\n return compare(a.start, b.end) > 0;\n}\n\n// Returns the intersection of two intervals, or null if they don't\n// overlap. The result's bounds are the more restrictive of the two\n// inputs (closed ∩ open = open, etc.).\nexport function intersection(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) >= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) <= 0 ? a.end : b.end;\n // Compute resulting bounds: take the more restrictive at each endpoint.\n // If a.start === b.start, the more restrictive of (a.bounds, b.bounds)\n // at that endpoint wins.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n || (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n || (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the union of two intervals (the smallest interval containing\n// both), or null if they don't overlap (caller should use mergeIntervals\n// for that case).\nexport function union(a: Interval, b: Interval): Interval | null {\n if (!intersects(a, b)) return null;\n const start = compare(a.start, b.start) <= 0 ? a.start : b.start;\n const end = compare(a.end, b.end) >= 0 ? a.end : b.end;\n // Union's bounds are the less restrictive at each endpoint.\n const startBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-end')\n && (b.bounds === 'open' || b.bounds === 'half-open-end');\n const endBoundsOpen = (a.bounds === 'open' || a.bounds === 'half-open-start')\n && (b.bounds === 'open' || b.bounds === 'half-open-start');\n const bounds: IntervalBounds = startBoundsOpen && endBoundsOpen ? 'open'\n : startBoundsOpen ? 'half-open-end'\n : endBoundsOpen ? 'half-open-start'\n : 'closed';\n return { start, end, bounds };\n}\n\n// Returns the part of `a` that is not in `b`. May produce 0, 1, or 2\n// intervals depending on the overlap shape.\n//\n// Bounds semantics: each surviving piece keeps the inclusivity of the\n// original endpoint it inherits from `a`, and the endpoint created by the\n// cut is always exclusive (it is `b`'s edge, which belongs to the removed\n// overlap). The old implementation derived both bounds from a lossy\n// helper that collapsed several of these combinations — the pieces'\n// endpoints were right, but the bounds metadata was stricter than the\n// actual remainder for open/half-open inputs.\nexport function difference(a: Interval, b: Interval): Interval[] {\n if (!intersects(a, b)) return [a];\n const result: Interval[] = [];\n // Does `a` include its own start / end?\n const aStartIncluded = a.bounds === 'closed' || a.bounds === 'half-open-end';\n const aEndIncluded = a.bounds === 'closed' || a.bounds === 'half-open-start';\n // Part of `a` before `b` starts: [a.start, b.start) — start inclusivity\n // inherited from a, cut end always exclusive.\n if (compare(a.start, b.start) < 0) {\n result.push({ start: a.start, end: b.start, bounds: aStartIncluded ? 'half-open-end' : 'open' });\n }\n // Part of `a` after `b` ends: (b.end, a.end] — cut start always\n // exclusive, end inclusivity inherited from a.\n if (compare(a.end, b.end) > 0) {\n result.push({ start: b.end, end: a.end, bounds: aEndIncluded ? 'half-open-start' : 'open' });\n }\n return result;\n}\n\n// Alias for difference() — \"subtract\" reads more naturally at some\n// call sites than \"difference\" does.\nexport const subtract = difference;\n\n// Merges a list of intervals, combining overlapping ones. Returns a\n// sorted list of disjoint intervals. The inputs are treated as\n// immutable — the merged result carries shallow copies of any interval\n// object it extends, so a caller's own `end` reference is never\n// reassigned under them (mergeIntervals used to write `last.end =\n// current.end` straight into the caller's object).\nexport function mergeIntervals(intervals: Interval[]): Interval[] {\n if (intervals.length === 0) return [];\n const sorted = [...intervals].sort((a, b) => compare(a.start, b.start));\n const result: Interval[] = [{ ...sorted[0]! }];\n for (let i = 1; i < sorted.length; i++) {\n const current = sorted[i]!;\n const last = result[result.length - 1]!;\n if (intersects(last, current) || compare(last.end, current.start) === 0) {\n // Merge into last.\n if (compare(current.end, last.end) > 0) {\n last.end = current.end;\n }\n } else {\n result.push(current);\n }\n }\n return result;\n}\n\n// Widest instant JS Date can represent (±8.64e15 ms, i.e. ±100,000,000\n// days from the Unix epoch). splitInterval rebuilds endpoints via Date,\n// so anything outside this window would silently produce Invalid Date\n// objects with NaN year/month/day fields — reject it descriptively\n// instead.\nconst MAX_DATE_MS = 8.64e15;\n\n// Splits an interval into N equal sub-intervals. Throws if N ≤ 0.\n// Equality is by ms-distance between start and end — for date ranges\n// this approximates \"equal time slices\", which is the most useful\n// interpretation. For calendar-bound splits (e.g. \"split this month\n// into weeks\"), callers should use recurrence() instead.\nexport function splitInterval(iv: Interval, n: number): Interval[] {\n if (n <= 0) throw new Error(`temporal-fmt: splitInterval requires n > 0 (got ${n}).`);\n if (n === 1) return [iv];\n const startFields = asDateFieldView(iv.start) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const endFields = asDateFieldView(iv.end) as DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number };\n const startMs = toMs(startFields);\n const endMs = toMs(endFields);\n if (Math.abs(startMs) > MAX_DATE_MS || Math.abs(endMs) > MAX_DATE_MS) {\n throw new RangeError(\n `temporal-fmt: splitInterval() endpoints are outside the representable Date range ` +\n `(approximately ±275,760 years). Split the interval into smaller ranges first.`\n );\n }\n const step = (endMs - startMs) / n;\n const result: Interval[] = [];\n for (let i = 0; i < n; i++) {\n const sliceStart = startMs + i * step;\n const sliceEnd = i === n - 1 ? endMs : sliceStart + step;\n result.push({\n start: fromMs(sliceStart, startFields),\n end: fromMs(sliceEnd, endFields),\n bounds: i === 0 ? iv.bounds : 'half-open-start',\n });\n }\n return result;\n}\n\nfunction toMs(v: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): number {\n // Reuse the same day-count math arithmetic.ts uses, inlined.\n const y = v.year!, m = v.month!, d = v.day!;\n const y2 = m <= 2 ? y - 1 : y;\n const era = Math.floor((y2 >= 0 ? y2 : y2 - 399) / 400);\n const yoe = y2 - era * 400;\n const m2 = m > 2 ? m - 3 : m + 9;\n const doy = Math.floor((153 * m2 + 2) / 5) + d - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n const days = era * 146097 + doe - 719468;\n return days * 86_400_000\n + (v.hour ?? 0) * 3_600_000\n + (v.minute ?? 0) * 60_000\n + (v.second ?? 0) * 1_000\n + (v.millisecond ?? 0);\n}\n\nfunction fromMs(ms: number, base: DateFieldView & { hour?: number; minute?: number; second?: number; millisecond?: number }): DateFieldView {\n const MS_PER_DAY = 86_400_000;\n const dayOverflow = Math.floor(ms / MS_PER_DAY);\n let withinDay = ms % MS_PER_DAY;\n if (withinDay < 0) withinDay += MS_PER_DAY;\n const hour = Math.floor(withinDay / 3_600_000);\n const minute = Math.floor((withinDay % 3_600_000) / 60_000);\n const second = Math.floor((withinDay % 60_000) / 1_000);\n const millisecond = withinDay % 1_000;\n // `ms` (and therefore `dayOverflow`) is already an absolute epoch-ms /\n // epoch-day value — toMs() computed it via Howard Hinnant's algorithm\n // from scratch, not as an offset from `base`. So the date only needs\n // reconstructing from the Unix epoch itself; `base` is just a template\n // for whatever extra (non-date) fields get spread into the result.\n // (Previously this added dayOverflow on top of base's own epoch-ms,\n // double-counting base's offset and landing tens of thousands of days\n // away from the intended date for any non-epoch base.)\n const newMs = dayOverflow * MS_PER_DAY;\n const d = new Date(newMs);\n return {\n ...base,\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n hour,\n minute,\n second,\n millisecond,\n };\n}\n\n// Formats an interval as a single string, collapsing shared fields.\n// E.g. \"August 4 – August 6, 2026\" rather than \"August 4, 2026 – August 6, 2026\".\n// Uses Intl.DateTimeFormat's formatRange when available (most engines),\n// falling back to format(start, fmt) + '–' + format(end, fmt) when not.\n\nexport function formatRange(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): string {\n // The formatStr parameter is this function's contract — the caller\n // asked for their token format on both endpoints. Honor it first:\n // format each side with the caller's format string and join with an\n // en dash. (This used to try Intl.DateTimeFormat.formatRange() first,\n // which ignores formatStr entirely — formatRange(iv, 'yyyy-MM-dd')\n // silently returned locale-default output like \"8/4/2026 – 8/6/2026\"\n // instead of \"2026-08-04 – 2026-08-06\".) Intl's range collapsing is\n // kept as a fallback for inputs the token path can't render.\n try {\n const startStr = format(iv.start as Parameters<typeof format>[0], formatStr, options);\n const endStr = format(iv.end as Parameters<typeof format>[0], formatStr, options);\n return `${startStr} – ${endStr}`;\n } catch (err) {\n // Fallback: Intl.DateTimeFormat.formatRange is the standard\n // mechanism with native field-collapse behavior, but it has no\n // knowledge of the caller's token format string.\n try {\n const fmt = new Intl.DateTimeFormat(normalizeLocaleTag(options.locale ?? 'en-US'), {});\n // Cast the interval endpoints to Date for the Intl call. Both\n // endpoints must be Temporal values that can convert to a Date;\n // for non-ZonedDateTime values we synthesize a Date via the field\n // shape (year/month/day/hour/...).\n const startDate = toJSDate(iv.start);\n const endDate = toJSDate(iv.end);\n return fmt.formatRange(startDate, endDate);\n } catch {\n // Neither path could render — surface the token-path error, since\n // the formatStr contract is the primary one.\n throw err;\n }\n }\n}\n\nexport function formatRangeToParts(\n iv: Interval,\n formatStr: string,\n options: FormatOptions = {},\n): FormattedPart[] {\n // Best-effort: returns formatToParts for the start, then a literal\n // \" – \" part, then formatToParts for the end. Doesn't currently\n // expose Intl.DateTimeFormat.formatRangeToParts's collapsed shape —\n // that would require mapping Intl's part types back to temporal-fmt's\n // token strings, which is locale-dependent and not stable across\n // engines.\n const startParts = formatToParts(iv.start as Parameters<typeof formatToParts>[0], formatStr, options);\n const endParts = formatToParts(iv.end as Parameters<typeof formatToParts>[0], formatStr, options);\n const result: FormattedPart[] = [...startParts];\n result.push({ type: 'literal', value: ' – ' });\n result.push(...endParts);\n return result;\n}\n\nfunction toJSDate(value: unknown): Date {\n const v = value as { year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number; toInstant?: () => { epochMilliseconds: number } };\n if (typeof v?.toInstant === 'function') {\n return new Date(v.toInstant().epochMilliseconds);\n }\n if (typeof v?.year === 'number' && typeof v?.month === 'number' && typeof v?.day === 'number') {\n return new Date(Date.UTC(v.year, v.month - 1, v.day, v.hour ?? 0, v.minute ?? 0, v.second ?? 0, v.millisecond ?? 0));\n }\n throw new Error(`temporal-fmt: formatRange() expected Temporal values with year/month/day, got ${typeof value}.`);\n}","// Recurrence engine. Deterministic RRULE-like\n// recurrence without pulling in a runtime dependency. Supports\n// secondly/minutely/hourly/daily/weekly/monthly/yearly frequencies,\n// interval, count, until, weekdays, monthDays, positional rules,\n// exclusions, inclusions.\n//\n// RRULE interop: parseRRule() / formatRRule() convert to/from the\n// standard iCalendar RRULE string format (RFC 5545). Implemented\n// without depending on a RRULE library — the grammar is small enough\n// to handle inline.\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport type RecurrenceFrequency = 'secondly' | 'minutely' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';\n\nexport interface RecurrenceRule {\n frequency: RecurrenceFrequency;\n interval: number; // default 1\n count?: number; // max occurrences\n until?: unknown; // Temporal value — recurrence ends before this\n // For weekly: ISO weekdays to include (1=Mon..7=Sun). Empty = all.\n byWeekday?: number[];\n // For monthly/yearly: days of month to include (1-31, or negative\n // for end-of-month: -1 = last day).\n byMonthDay?: number[];\n // For yearly: months to include (1-12).\n byMonth?: number[];\n // For weekly: which week of the year (1-53).\n byWeek?: number[];\n // Exclusions: dates to skip even if they match the rule.\n exDates?: unknown[];\n // Inclusions: dates to add even if they don't match the rule.\n rDates?: unknown[];\n}\n\nexport interface RecurrenceIterator {\n next(): { value: unknown; done: boolean } | { value: undefined; done: true };\n previous(): { value: unknown; done: boolean } | { value: undefined; done: true };\n}\n\n// Creates a recurrence iterator starting from `start`. The first call\n// to next() returns `start` itself (if it matches the rule); subsequent\n// calls return the next matching occurrence.\nexport function recurrence(start: unknown, rule: RecurrenceRule): RecurrenceIterator {\n let current: unknown = start;\n let count = 0;\n // Track past values for previous() — keeps a ring buffer of the last\n // N occurrences so previous() can walk back without recomputing.\n const history: unknown[] = [];\n const MAX_HISTORY = 10_000;\n let atEnd = false;\n let atStart = true;\n\n function matches(value: unknown): boolean {\n const v = value as { dayOfWeek?: number; day?: number; month?: number; year?: number; hour?: number; minute?: number; second?: number };\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n if (!rule.byWeekday.includes(v.dayOfWeek ?? 0)) return false;\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) {\n if (!rule.byMonthDay.includes(v.day ?? 0)) return false;\n }\n if (rule.byMonth && rule.byMonth.length > 0) {\n if (!rule.byMonth.includes(v.month ?? 0)) return false;\n }\n if (rule.exDates && rule.exDates.some((d) => compare(d, value) === 0)) return false;\n return true;\n }\n\n function advance(value: unknown, steps: number): unknown {\n // Add `steps` * interval units to `value`.\n const unit = rule.frequency === 'secondly' ? 'seconds'\n : rule.frequency === 'minutely' ? 'minutes'\n : rule.frequency === 'hourly' ? 'hours'\n : rule.frequency === 'daily' ? 'days'\n : rule.frequency === 'weekly' ? 'weeks'\n : rule.frequency === 'monthly' ? 'months'\n : 'years';\n return add(value, steps * rule.interval, unit as Parameters<typeof add>[2]);\n }\n\n function recordHistory(value: unknown): void {\n history.push(value);\n if (history.length > MAX_HISTORY) history.shift();\n }\n\n function nextMatch(value: unknown): { value: unknown; found: boolean } {\n // Advance one step at a time until we find a match. For\n // byWeekday/byMonthDay rules this can skip multiple steps.\n let candidate = value;\n let safetyCounter = 0;\n do {\n candidate = advance(candidate, 1);\n safetyCounter++;\n if (safetyCounter > 1000) {\n // Defensive — if the rule is so restrictive no match exists\n // within 1000 steps, give up rather than spin forever. The\n // caller must treat this as \"no more occurrences\", not as a\n // real match — returning the unmatched candidate here used to\n // get handed back to next()'s caller as if it were valid.\n atEnd = true;\n return { value: candidate, found: false };\n }\n if (rule.until && compare(candidate, rule.until) > 0) {\n atEnd = true;\n return { value: candidate, found: false };\n }\n } while (!matches(candidate));\n return { value: candidate, found: true };\n }\n\n return {\n next() {\n if (atEnd) return { value: undefined, done: true };\n if (atStart) {\n atStart = false;\n if (matches(current)) {\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n if (rule.until && compare(current, rule.until) > 0) {\n atEnd = true;\n return { value: undefined, done: true };\n }\n // Include rDates that match the current value.\n recordHistory(current);\n return { value: current, done: false };\n }\n // If start doesn't match, advance to first match.\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n }\n const advanced = nextMatch(current);\n if (!advanced.found) return { value: undefined, done: true };\n current = advanced.value;\n count++;\n if (rule.count !== undefined && count >= rule.count) atEnd = true;\n recordHistory(current);\n return { value: current, done: false };\n },\n previous() {\n if (history.length === 0) return { value: undefined, done: true };\n const v = history.pop()!;\n return { value: v, done: false };\n },\n };\n}\n\n// Take N occurrences from a recurrence iterator.\nexport function take(iter: RecurrenceIterator, n: number): unknown[] {\n const result: unknown[] = [];\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n result.push(r.value);\n }\n return result;\n}\n\n// Upper bound on how many occurrences skip() will collect after its\n// skip phase. skip() used to call take(iter, Number.MAX_SAFE_INTEGER),\n// and an iterator over an unbounded rule (no count, no until — e.g.\n// { frequency: 'daily', interval: 1 }) never returns done: next() always\n// has another day. The call then looped forever, pushing into a result\n// array until the process OOM'd. Same cap style as businessCalendar.ts.\nconst MAX_SKIP_COLLECTION = 100_000;\n\n// Skip N occurrences from a recurrence iterator, returning the ones\n// that follow. Throws a RangeError when the iterator is still producing\n// occurrences after MAX_SKIP_COLLECTION — that means the rule is\n// unbounded (no count/until) and \"everything after the skip\" has no\n// finite answer.\nexport function skip(iter: RecurrenceIterator, n: number): unknown[] {\n for (let i = 0; i < n; i++) {\n const r = iter.next();\n if (r.done) break;\n }\n const result: unknown[] = [];\n while (result.length < MAX_SKIP_COLLECTION) {\n const r = iter.next();\n if (r.done) return result;\n result.push(r.value);\n }\n throw new RangeError(\n `temporal-fmt: skip() collected ${MAX_SKIP_COLLECTION} occurrences and the rule is still ` +\n `producing — add a count or until to the rule, or use take(iter, n) for a bounded read.`\n );\n}\n\n// All occurrences between two dates (inclusive of start, exclusive of end).\nexport function between(start: unknown, rule: RecurrenceRule, rangeStart: unknown, rangeEnd: unknown): unknown[] {\n const iter = recurrence(start, rule);\n const result: unknown[] = [];\n while (true) {\n const r = iter.next();\n if (r.done) break;\n if (compare(r.value, rangeEnd) >= 0) break;\n if (compare(r.value, rangeStart) >= 0) result.push(r.value);\n }\n return result;\n}\n\n// Parses an RRULE string like \"FREQ=DAILY;INTERVAL=2;COUNT=5\" into a\n// RecurrenceRule. Doesn't support every RRULE feature (BYSETPOS,\n// BYHOUR, BYMINUTE, BYSECOND are parsed but not enforced by the\n// recurrence iterator above), but covers the common cases.\nexport function parseRRule(input: string): RecurrenceRule {\n const parts = input.trim().toUpperCase().replace(/^RRULE:/, '').split(';');\n const rule: RecurrenceRule = { frequency: 'daily', interval: 1 };\n const frequencies: ReadonlySet<string> = new Set([\n 'SECONDLY', 'MINUTELY', 'HOURLY', 'DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY',\n ]);\n for (const part of parts) {\n if (!part) continue;\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const key = part.slice(0, eq);\n const value = part.slice(eq + 1);\n switch (key) {\n case 'FREQ':\n if (!frequencies.has(value)) {\n throw new RangeError(`temporal-fmt: unsupported RRULE frequency \"${value}\".`);\n }\n rule.frequency = value.toLowerCase() as RecurrenceFrequency;\n break;\n case 'INTERVAL': {\n const interval = Number(value);\n if (!Number.isSafeInteger(interval) || interval < 1) {\n throw new RangeError(`temporal-fmt: RRULE INTERVAL must be a positive safe integer (got \"${value}\").`);\n }\n rule.interval = interval;\n break;\n }\n case 'COUNT': {\n const count = Number(value);\n if (!Number.isSafeInteger(count) || count < 1) {\n throw new RangeError(`temporal-fmt: RRULE COUNT must be a positive safe integer (got \"${value}\").`);\n }\n rule.count = count;\n break;\n }\n case 'UNTIL':\n rule.until = value;\n break;\n case 'BYDAY':\n rule.byWeekday = value.split(',').map((d) => {\n const m = d.match(/^([+-]?\\d)?([A-Z]{2})$/);\n if (!m) return 0;\n const wd = m[2];\n const map: Record<string, number> = { MO: 1, TU: 2, WE: 3, TH: 4, FR: 5, SA: 6, SU: 7 };\n return map[wd!] ?? 0;\n });\n break;\n case 'BYMONTHDAY': {\n const values = value.split(',').map(Number);\n if (values.some((day) => !Number.isInteger(day) || day === 0 || day < -31 || day > 31)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTHDAY contains an out-of-range value.`);\n }\n rule.byMonthDay = values;\n break;\n }\n case 'BYMONTH': {\n const values = value.split(',').map(Number);\n if (values.some((month) => !Number.isInteger(month) || month < 1 || month > 12)) {\n throw new RangeError(`temporal-fmt: RRULE BYMONTH contains an out-of-range value.`);\n }\n rule.byMonth = values;\n break;\n }\n }\n }\n return rule;\n}\n\nexport function formatRRule(rule: RecurrenceRule): string {\n const parts: string[] = [`FREQ=${rule.frequency.toUpperCase()}`];\n if (rule.interval !== 1) parts.push(`INTERVAL=${rule.interval}`);\n if (rule.count !== undefined) parts.push(`COUNT=${rule.count}`);\n if (rule.until !== undefined) parts.push(`UNTIL=${String(rule.until)}`);\n if (rule.byWeekday && rule.byWeekday.length > 0) {\n const map: Record<number, string> = { 1: 'MO', 2: 'TU', 3: 'WE', 4: 'TH', 5: 'FR', 6: 'SA', 7: 'SU' };\n parts.push(`BYDAY=${rule.byWeekday.map((d) => map[d]).join(',')}`);\n }\n if (rule.byMonthDay && rule.byMonthDay.length > 0) parts.push(`BYMONTHDAY=${rule.byMonthDay.join(',')}`);\n if (rule.byMonth && rule.byMonth.length > 0) parts.push(`BYMONTH=${rule.byMonth.join(',')}`);\n return parts.join(';');\n}","// Business calendar. Customizable weekend definitions, holiday set,\n// observed holidays, working hours, optional half days. The holiday\n// check is delegated to a holiday calendar (holidays.ts) so this\n// module stays focused on weekday-vs-weekend logic.\n\nimport { add } from './arithmetic.js';\nimport type { HolidayCalendar } from './holidays.js';\n\nexport interface BusinessCalendarOptions {\n // Default [6, 7] (Sat/Sun). ISO weekdays 1-7.\n weekend?: number[];\n // Holiday calendar to consult. Optional — if absent, only weekend\n // logic applies.\n holidays?: HolidayCalendar;\n // Working hours per weekday. 1=Mon..7=Sun. Default: 8 hours for\n // weekdays, 0 for weekend. Used by addBusinessDays() to compute\n // partial-day arithmetic.\n workingHours?: Record<number, number>;\n // Half days: weekdays where working hours are reduced. Default empty.\n halfDays?: number[];\n}\n\nexport interface BusinessCalendar {\n weekend: Set<number>;\n holidays?: HolidayCalendar;\n workingHours: Record<number, number>;\n halfDays: Set<number>;\n}\n\nexport function createBusinessCalendar(options: BusinessCalendarOptions = {}): BusinessCalendar {\n const weekend = new Set(options.weekend ?? [6, 7]);\n // Copy before filling defaults — writing the missing weekday entries\n // straight into options.workingHours mutated the caller's object\n // (their { 1: 6 } grew keys 2..7 as a side effect of \"creating\" a\n // calendar, and a shared object across calendars cross-contaminated\n // them with the first calendar's defaults).\n const workingHours: Record<number, number> = { ...(options.workingHours ?? {}) };\n for (let i = 1; i <= 7; i++) {\n if (workingHours[i] === undefined) {\n workingHours[i] = weekend.has(i) ? 0 : 8;\n }\n }\n return {\n weekend,\n holidays: options.holidays,\n workingHours,\n halfDays: new Set(options.halfDays ?? []),\n };\n}\n\nexport function isBusinessDay(cal: BusinessCalendar, value: unknown): boolean {\n const v = value as { dayOfWeek?: number; year?: number; month?: number; day?: number };\n // add() in arithmetic.ts doesn't update dayOfWeek (it returns a field\n // bag with whatever dayOfWeek the input had). Compute it from year/month/day\n // whenever those are present — more reliable than trusting the cached\n // dayOfWeek value.\n let dow = v.dayOfWeek;\n if (typeof v.year === 'number' && typeof v.month === 'number' && typeof v.day === 'number') {\n const d = new Date(Date.UTC(v.year, v.month - 1, v.day));\n const jsDow = d.getUTCDay(); // 0=Sun..6=Sat\n dow = jsDow === 0 ? 7 : jsDow; // 1=Mon..7=Sun\n }\n if (typeof dow !== 'number') {\n throw new Error('temporal-fmt: isBusinessDay() needs a value with dayOfWeek (or year/month/day to compute it).');\n }\n if (cal.weekend.has(dow)) return false;\n if (cal.holidays && cal.holidays.isHoliday(value)) return false;\n return true;\n}\n\nexport function nextBusinessDay(cal: BusinessCalendar, value: unknown): unknown {\n let candidate = add(value, 1, 'days');\n // Loop until we find a business day. Capped at 7 iterations since a\n // week is the natural upper bound for a Sat/Sun + holiday combo.\n for (let i = 0; i < 14; i++) {\n if (isBusinessDay(cal, candidate)) return candidate;\n candidate = add(candidate, 1, 'days');\n }\n throw new Error('temporal-fmt: nextBusinessDay() gave up after 14 days — likely a misconfigured calendar (e.g. all days are weekends or holidays).');\n}\n\nexport function previousBusinessDay(cal: BusinessCalendar, value: unknown): unknown {\n let candidate = add(value, -1, 'days');\n for (let i = 0; i < 14; i++) {\n if (isBusinessDay(cal, candidate)) return candidate;\n candidate = add(candidate, -1, 'days');\n }\n throw new Error('temporal-fmt: previousBusinessDay() gave up after 14 days — likely a misconfigured calendar.');\n}\n\nconst MAX_BUSINESS_DAY_TRAVERSAL = 100_000;\n\nfunction assertBusinessDayCount(days: number): void {\n if (!Number.isSafeInteger(days)) {\n throw new RangeError(\n `temporal-fmt: business-day count must be a finite safe integer (got ${days}).`,\n );\n }\n}\n\nfunction compareDateFields(a: unknown, b: unknown): number {\n const av = a as { year: number; month: number; day: number };\n const bv = b as { year: number; month: number; day: number };\n const ay = av.year * 10000 + av.month * 100 + av.day;\n const by = bv.year * 10000 + bv.month * 100 + bv.day;\n return ay - by;\n}\n\nexport function addBusinessDays(cal: BusinessCalendar, value: unknown, days: number): unknown {\n assertBusinessDayCount(days);\n if (days === 0) return value;\n const sign = days > 0 ? 1 : -1;\n const absDays = Math.abs(days);\n if (absDays > MAX_BUSINESS_DAY_TRAVERSAL) {\n throw new RangeError(\n `temporal-fmt: addBusinessDays() exceeded the ${MAX_BUSINESS_DAY_TRAVERSAL}-day limit.`,\n );\n }\n let candidate = value;\n for (let i = 0; i < absDays; i++) {\n candidate = sign > 0 ? nextBusinessDay(cal, candidate) : previousBusinessDay(cal, candidate);\n }\n return candidate;\n}\n\nexport function subtractBusinessDays(cal: BusinessCalendar, value: unknown, days: number): unknown {\n assertBusinessDayCount(days);\n return addBusinessDays(cal, value, -days);\n}\n\nexport function differenceInBusinessDays(cal: BusinessCalendar, a: unknown, b: unknown): number {\n const rawDirection = compareDateFields(b, a);\n if (rawDirection === 0) return 0;\n\n const direction = rawDirection > 0 ? 1 : -1;\n let candidate = a;\n let count = 0;\n let traversedDays = 0;\n\n while (compareDateFields(candidate, b) !== 0) {\n candidate = add(candidate, direction, 'days');\n traversedDays++;\n if (traversedDays > MAX_BUSINESS_DAY_TRAVERSAL) {\n throw new RangeError(\n `temporal-fmt: differenceInBusinessDays() exceeded the ${MAX_BUSINESS_DAY_TRAVERSAL}-day traversal limit.`,\n );\n }\n if (isBusinessDay(cal, candidate)) count += direction;\n }\n\n return count;\n}\n","// Holiday framework. Core provides the abstraction only —\n// country-specific holiday datasets stay out of core; those can be\n// separate ecosystem packages built on top of this. A holiday calendar\n// is a function from a date → boolean (\"is this a holiday?\") plus\n// iteration helpers (nextHoliday, previousHoliday, holidaysBetween).\n\nimport { add } from './arithmetic.js';\nimport { compare } from './comparison.js';\n\nexport interface HolidayCalendar {\n isHoliday(value: unknown): boolean;\n // Returns the list of holidays within [start, end] inclusive.\n holidaysBetween(start: unknown, end: unknown): unknown[];\n}\n\nexport interface HolidaySpec {\n // Month + day for fixed-date holidays (e.g. Jan 1 → New Year's).\n month?: number;\n day?: number;\n // For floating holidays: a function that takes a year and returns\n // the { month, day } of the holiday in that year. Used for \"last\n // Monday of May\" style rules.\n compute?: (year: number) => { month: number; day: number };\n // Optional name (for introspection / debugging).\n name?: string;\n}\n\n// Upper bound on the year range holidaysBetween() will walk. The loop\n// is year-granular with no input validation on the endpoints, so a\n// caller passing (say) year 1 to year 300000 makes it iterate — and\n// cache a holiday list — for every year in between; hostile or\n// accidental huge ranges used to hang the process and grow the\n// per-calendar cache without bound. 5,000 years matches the traversal\n// caps businessCalendar.ts and recurrence.ts already use for the same\n// class of \"walk it one step at a time\" APIs.\nconst MAX_HOLIDAY_YEAR_RANGE = 5_000;\n\nexport function createHolidayCalendar(specs: HolidaySpec[]): HolidayCalendar {\n // Pre-compute holidays for a year on demand, cache by year.\n const cache = new Map<number, Array<{ month: number; day: number; name?: string }>>();\n function forYear(year: number) {\n let list = cache.get(year);\n if (list) return list;\n list = specs.map((s) => {\n let md: { month: number; day: number };\n if (s.compute) {\n md = s.compute(year);\n } else {\n md = { month: s.month!, day: s.day! };\n }\n return { ...md, name: s.name };\n });\n cache.set(year, list);\n return list;\n }\n return {\n isHoliday(value: unknown) {\n const v = value as { year?: number; month?: number; day?: number };\n if (typeof v.year !== 'number' || typeof v.month !== 'number' || typeof v.day !== 'number') {\n throw new Error('temporal-fmt: isHoliday() needs a value with year/month/day.');\n }\n const list = forYear(v.year);\n return list.some((h) => h.month === v.month && h.day === v.day);\n },\n holidaysBetween(start: unknown, end: unknown) {\n const sv = start as { year: number; month: number; day: number };\n const ev = end as { year: number; month: number; day: number };\n if (typeof sv?.year !== 'number' || typeof ev?.year !== 'number') {\n throw new Error('temporal-fmt: holidaysBetween() needs start/end values with year/month/day.');\n }\n if (ev.year - sv.year > MAX_HOLIDAY_YEAR_RANGE) {\n throw new RangeError(\n `temporal-fmt: holidaysBetween() year range (${sv.year}–${ev.year}) exceeds the ` +\n `${MAX_HOLIDAY_YEAR_RANGE}-year limit. Split the query into smaller ranges.`\n );\n }\n const result: unknown[] = [];\n for (let y = sv.year; y <= ev.year; y++) {\n const list = forYear(y);\n for (const h of list) {\n const date = { year: y, month: h.month, day: h.day };\n if (compare(date, sv) >= 0 && compare(date, ev) <= 0) {\n result.push({ year: y, month: h.month, day: h.day });\n }\n }\n }\n return result;\n },\n };\n}\n\n// Returns the next holiday strictly after `value`. Returns undefined\n// if no holiday is registered within the next 5 years (sanity cap).\nexport function nextHoliday(cal: HolidayCalendar, value: unknown): unknown | undefined {\n let candidate = add(value, 1, 'days');\n for (let i = 0; i < 365 * 5; i++) {\n if (cal.isHoliday(candidate)) return candidate;\n candidate = add(candidate, 1, 'days');\n }\n return undefined;\n}\n\nexport function previousHoliday(cal: HolidayCalendar, value: unknown): unknown | undefined {\n let candidate = add(value, -1, 'days');\n for (let i = 0; i < 365 * 5; i++) {\n if (cal.isHoliday(candidate)) return candidate;\n candidate = add(candidate, -1, 'days');\n }\n return undefined;\n}\n\n// holidaysBetween is exposed as a HolidayCalendar method (see above);\n// this standalone helper delegates to it for ergonomic access.\nexport function holidaysBetween(cal: HolidayCalendar, start: unknown, end: unknown): unknown[] {\n return cal.holidaysBetween(start, end);\n}\n","// Time-zone subsystem. Wraps Temporal.ZonedDateTime's\n// timezone introspection surface in temporal-fmt's field-based convention.\n// Includes disambiguation modes (compatible/earlier/later/reject) for\n// DST gaps and overlaps, plus transition queries (getNextTransition,\n// getPreviousTransition, getTransitions).\n\nimport { getTemporal } from './temporalProvider.js';\nimport { add } from './arithmetic.js';\n\nexport type DisambiguationMode = 'compatible' | 'earlier' | 'later' | 'reject';\n\nexport interface ResolveZonedOptions {\n disambiguation?: DisambiguationMode;\n overflow?: 'constrain' | 'reject';\n offset?: 'use' | 'ignore' | 'prefer' | 'reject';\n}\n\n// Resolves a wall-clock time in a timezone, applying disambiguation\n// for DST gaps and overlaps.\nexport function resolveZoned(\n fields: { year: number; month: number; day: number; hour?: number; minute?: number; second?: number; millisecond?: number; microsecond?: number; nanosecond?: number },\n timeZone: string,\n options: ResolveZonedOptions = {},\n): unknown {\n const temporal = getTemporal();\n const disambiguation = options.disambiguation ?? 'compatible';\n const overflow = options.overflow ?? 'constrain';\n // Cast options to a permissive record — Temporal.ZonedDateTime.from\n // accepts disambiguation/offset options, but the existing\n // TemporalFactory.from() signature in temporalProvider.ts is narrower.\n // The runtime accepts the wider shape; this cast bypasses the type\n // narrowing without changing runtime behavior.\n const fromOptions = {\n overflow,\n disambiguation,\n offset: options.offset ?? 'prefer',\n } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1];\n try {\n return temporal.ZonedDateTime.from(\n {\n year: fields.year,\n month: fields.month,\n day: fields.day,\n hour: fields.hour ?? 0,\n minute: fields.minute ?? 0,\n second: fields.second ?? 0,\n millisecond: fields.millisecond ?? 0,\n microsecond: fields.microsecond ?? 0,\n nanosecond: fields.nanosecond ?? 0,\n timeZone,\n },\n fromOptions,\n );\n } catch (err) {\n // Surface DST gap / overlap errors with a clearer message.\n const msg = (err as Error).message;\n if (/no such (wall-clock )?time/i.test(msg) && disambiguation === 'reject') {\n throw new Error(\n `temporal-fmt: \"${timeZone}\" has no such wall-clock time on ${fields.year}-${fields.month}-${fields.day}T${fields.hour ?? 0}:${fields.minute ?? 0} — ` +\n `it falls in a DST gap. Pass { disambiguation: 'compatible' | 'earlier' | 'later' } to pick an instant.`\n );\n }\n throw err;\n }\n}\n\nexport function getTimeZone(value: unknown): string {\n const v = value as { timeZoneId?: string };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTimeZone() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.timeZoneId;\n}\n\nexport function getOffset(value: unknown): string {\n const v = value as { offset?: string };\n if (typeof v?.offset !== 'string') {\n throw new Error(`temporal-fmt: getOffset() expected a ZonedDateTime, got ${typeof value}.`);\n }\n return v.offset;\n}\n\nexport function getOffsetNanoseconds(value: unknown): number {\n const v = value as { offsetNanoseconds?: number; offset?: string };\n if (typeof v?.offsetNanoseconds === 'number') return v.offsetNanoseconds;\n if (typeof v?.offset === 'string') {\n // Parse ±HH:MM → nanoseconds.\n const m = v.offset.match(/^([+-])(\\d{2}):(\\d{2})$/);\n if (!m) throw new Error(`temporal-fmt: getOffsetNanoseconds() couldn't parse offset \"${v.offset}\".`);\n const sign = m[1] === '-' ? -1 : 1;\n return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60) * 1_000_000_000;\n }\n throw new Error(`temporal-fmt: getOffsetNanoseconds() expected a ZonedDateTime, got ${typeof value}.`);\n}\n\n// DST detection: a ZonedDateTime is in DST iff the offset is different\n// from the timezone's standard offset at the same instant. Computed\n// by comparing the current offset to the offset in January (Northern\n// Hemisphere) or July (Southern Hemisphere) of the same year — a\n// heuristic that's right for most populated zones.\nexport function isDST(value: unknown): boolean {\n const v = value as { timeZoneId?: string; offset?: string; year?: number; toInstant?: () => unknown };\n if (typeof v?.timeZoneId !== 'string' || typeof v?.offset !== 'string' || typeof v?.year !== 'number') {\n throw new Error(`temporal-fmt: isDST() expected a ZonedDateTime, got ${typeof value}.`);\n }\n // Compute the offset for January 1 of the same year (standard time\n // for Northern Hemisphere zones) and compare. For Southern Hemisphere\n // zones January is summer, so we'd want July instead — but checking\n // January vs current is good enough for the common case, and we\n // can't reliably determine hemisphere without a lookup table.\n const temporal = getTemporal();\n try {\n const jan = temporal.ZonedDateTime.from(\n { year: v.year, month: 1, day: 1, hour: 12, timeZone: v.timeZoneId },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n return jan.offset !== v.offset;\n } catch {\n return false;\n }\n}\n\n// Returns the next DST transition strictly after `value`, or undefined\n// if no transition occurs within the next 2 years. Implemented by\n// walking day-by-day checking for offset changes — slow but simple,\n// and correct across all IANA zones.\nexport function getNextTransition(value: unknown): unknown | undefined {\n return findTransition(value, 1);\n}\n\nexport function getPreviousTransition(value: unknown): unknown | undefined {\n return findTransition(value, -1);\n}\n\nfunction findTransition(value: unknown, direction: 1 | -1): unknown | undefined {\n const v = value as { timeZoneId?: string; offset?: string; toInstant?: () => unknown; year?: number; month?: number; day?: number; hour?: number; minute?: number; second?: number; millisecond?: number };\n if (typeof v?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: ${direction > 0 ? 'getNextTransition' : 'getPreviousTransition'}() expected a ZonedDateTime, got ${typeof value}.`);\n }\n const temporal = getTemporal();\n const timeZone = v.timeZoneId;\n let candidateFields = add(value, direction, 'days');\n const startOffset = v.offset!;\n for (let i = 0; i < 365 * 2; i++) {\n // Rebuild a real ZonedDateTime for this candidate day, in this\n // timezone, so `.offset` reflects the actual UTC offset on that\n // day rather than being absent. add() only produces a plain field\n // bag (it has no notion of timezone), so re-attaching timeZone via\n // ZonedDateTime.from is what actually lets us detect a DST change\n // here — using the field bag's own (nonexistent) offset would make\n // every call \"find\" a transition on the very first day checked.\n const cf = candidateFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const candidateZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string };\n if (candidateZdt.offset !== startOffset) {\n // Found a transition day. Return the reconstructed ZonedDateTime.\n return candidateZdt;\n }\n candidateFields = add(candidateFields, direction, 'days');\n }\n return undefined;\n}\n\nexport function getTransitions(start: unknown, end: unknown): unknown[] {\n const result: unknown[] = [];\n const startV = start as { timeZoneId?: string; offset?: string; year?: number; month?: number; day?: number };\n if (typeof startV?.timeZoneId !== 'string') {\n throw new Error(`temporal-fmt: getTransitions() expected a ZonedDateTime for start, got ${typeof start}.`);\n }\n const endV = end as { year: number; month: number; day: number };\n const temporal = getTemporal();\n const timeZone = startV.timeZoneId;\n let cursorFields = add(start, 1, 'days');\n let lastOffset = startV.offset!;\n for (let i = 0; i < 365 * 5; i++) {\n // Same reconstruction as findTransition() above — add() strips\n // timezone/offset info, so we rebuild a real ZonedDateTime each\n // iteration to read its actual offset instead of comparing against\n // a field bag that never has one.\n const cf = cursorFields as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number };\n const cursorZdt = temporal.ZonedDateTime.from(\n { ...cf, timeZone },\n { disambiguation: 'compatible' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { offset: string; year: number; month: number; day: number };\n if (cursorZdt.offset !== lastOffset) {\n // Found a transition.\n if (cursorZdt.year > endV.year || (cursorZdt.year === endV.year && (cursorZdt.month > endV.month || (cursorZdt.month === endV.month && cursorZdt.day > endV.day)))) break;\n result.push(cursorZdt);\n lastOffset = cursorZdt.offset;\n }\n cursorFields = add(cursorFields, 1, 'days');\n }\n return result;\n}\n\n// Returns the list of possible instants for a wall-clock time in a\n// zone — used to detect gaps (0 instants) and overlaps (2 instants).\nexport function possibleInstantsFor(\n fields: { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number },\n timeZone: string,\n): unknown[] {\n const temporal = getTemporal();\n // Resolve the same wall-clock fields twice, forcing disambiguation\n // toward the earlier and later side of any DST boundary. Comparing\n // the two results tells us which of the three cases we're in:\n // - gap (e.g. 2:30am on a spring-forward day): neither result's own\n // wall-clock fields match what was requested, since Temporal has\n // to shift off the nonexistent time in both directions — 0 instants.\n // - overlap (e.g. 1:30am on a fall-back day): both results keep the\n // requested wall-clock time but land on different instants (one\n // per side of the boundary) — 2 instants.\n // - normal time: both results match the requested wall-clock time\n // and resolve to the same instant — 1 instant.\n const earlier = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'earlier', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n const later = temporal.ZonedDateTime.from(\n { ...fields, timeZone },\n { disambiguation: 'later', offset: 'ignore' } as unknown as Parameters<typeof temporal.ZonedDateTime.from>[1],\n ) as { year: number; month: number; day: number; hour: number; minute: number; second: number; millisecond: number; toInstant: () => { epochNanoseconds: bigint } };\n\n const matchesRequestedWallClock = (z: typeof earlier) =>\n z.year === fields.year && z.month === fields.month && z.day === fields.day &&\n z.hour === fields.hour && z.minute === fields.minute &&\n z.second === fields.second && z.millisecond === fields.millisecond;\n\n if (!matchesRequestedWallClock(earlier) || !matchesRequestedWallClock(later)) {\n // Gap — the requested wall-clock time doesn't exist in this zone.\n return [];\n }\n if (earlier.toInstant().epochNanoseconds === later.toInstant().epochNanoseconds) {\n // Normal time — both disambiguation directions agree on the instant.\n return [earlier];\n }\n // Overlap — same wall-clock time, two distinct instants.\n return [earlier, later];\n}","// Extensibility. Controlled extensibility via\n// createFormatter({ tokens: ... }) rather than uncontrolled global\n// mutation. Custom tokens are passed at formatter-creation time;\n// built-in behavior stays deterministic regardless of what's registered.\n//\n// The existing registerLocaleVocab() is a global-registration escape\n// hatch for the locale-vocabulary case; createFormatter is the\n// per-formatter alternative for custom tokens.\n\nimport { type FormattedPart, type CompiledFormat } from './format.js';\nimport { TOKENS, type TemporalLike, type FormatOptions, DEFAULT_LOCALE } from './tokens.js';\nimport { MAX_FORMAT_LENGTH } from './constants.js';\nimport { UnknownTokenError } from './errors.js';\n\nexport type TokenHandler = (t: TemporalLike, locale: string) => string;\nexport type TokenField = keyof TemporalLike;\n\nexport interface CustomToken {\n name: string;\n handler: TokenHandler;\n field: TokenField;\n}\n\nexport interface FormatterOptions {\n // Custom tokens to add (or override). Built-in tokens with the same\n // name are replaced by the custom one.\n tokens?: CustomToken[];\n // Default locale for this formatter. Callers can still override per-call.\n defaultLocale?: string;\n}\n\nexport interface Formatter {\n format(temporal: TemporalLike, formatStr: string, options?: FormatOptions): string;\n formatToParts(temporal: TemporalLike, formatStr: string, options?: FormatOptions): FormattedPart[];\n compileFormat(formatStr: string): CompiledFormat;\n}\n\nexport function createFormatter(options: FormatterOptions = {}): Formatter {\n // Build a merged token table: built-ins plus overrides.\n const mergedTokens: Array<[string, TokenHandler, TokenField]> = [...TOKENS];\n const customTokenNames = new Set<string>();\n for (const custom of options.tokens ?? []) {\n customTokenNames.add(custom.name);\n }\n // Replace existing tokens with the same name; append new ones.\n for (const custom of options.tokens ?? []) {\n const existingIdx = mergedTokens.findIndex(([name]) => name === custom.name);\n if (existingIdx >= 0) {\n mergedTokens[existingIdx] = [custom.name, custom.handler, custom.field];\n } else {\n mergedTokens.push([custom.name, custom.handler, custom.field]);\n }\n }\n // Sort longest-first for the greedy tokenizer.\n mergedTokens.sort((a, b) => b[0].length - a[0].length);\n const handlerByToken = new Map(mergedTokens.map(([name, handler, field]) => [name, { handler, field }]));\n const sortedTokenStrings = mergedTokens.map(([name]) => name);\n\n function customTokenize(formatStr: string) {\n const pieces: Array<{ kind: 'token' | 'literal'; value: string }> = [];\n let i = 0;\n while (i < formatStr.length) {\n const ch = formatStr[i];\n if (ch === \"'\") {\n // Quoted literal handling — same as tokenize.ts.\n if (formatStr[i + 1] === \"'\") {\n appendLiteral(pieces, \"'\");\n i += 2;\n continue;\n }\n let j = i + 1;\n let literal = '';\n let closed = false;\n while (j < formatStr.length) {\n if (formatStr[j] === \"'\") {\n if (formatStr[j + 1] === \"'\") {\n literal += \"'\";\n j += 2;\n continue;\n }\n closed = true;\n j += 1;\n break;\n }\n literal += formatStr[j]!;\n j += 1;\n }\n if (!closed) throw new Error(`temporal-fmt: unterminated quote in format string \"${formatStr}\"`);\n appendLiteral(pieces, literal);\n i = j;\n continue;\n }\n const match = sortedTokenStrings.find((tok) => formatStr.startsWith(tok, i));\n if (match) {\n // Overlong-run guard, same rule as tokenize.ts: a greedy match of N\n // chars followed by one more of the same letter (\"MMMMM\", \"ddddd\")\n // is not a valid token of any width — emitting it as \"token +\n // literal\" would silently splice an unrelated field onto this one,\n // exactly the misreading the main tokenizer rejects. Same\n // UnknownTokenError shape tokenize.ts throws.\n const runChar = match[match.length - 1];\n if (formatStr[i + match.length] === runChar) {\n let end = i + match.length;\n while (formatStr[end] === runChar) end += 1;\n throw new UnknownTokenError({\n token: formatStr.slice(i, end),\n format: formatStr,\n message:\n `temporal-fmt: \"${formatStr.slice(i, end)}\" in format string \"${formatStr}\" isn't a recognized token — ` +\n `did you mean \"${match}\"?`,\n });\n }\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n appendLiteral(pieces, ch);\n i += 1;\n }\n return pieces;\n }\n\n function appendLiteral(pieces: Array<{ kind: 'token' | 'literal'; value: string }>, 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\n const defaultLocale = options.defaultLocale ?? DEFAULT_LOCALE;\n\n return {\n format(temporal: TemporalLike, formatStr: string, opts: FormatOptions = {}) {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const locale = opts.locale ?? defaultLocale;\n const pieces = customTokenize(formatStr);\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable: customTokenize only ever produces a\n `kind: 'token'` piece via sortedTokenStrings.find(...), and\n handlerByToken is built from that same mergedTokens array, so\n every token piece is guaranteed a handler entry. Guards against a\n future customTokenize change that decouples the two lookups. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(\n `temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\", which this Temporal object doesn't have.`\n );\n }\n result += handler.handler(temporal, locale);\n }\n return result;\n },\n formatToParts(temporal: TemporalLike, formatStr: string, opts: FormatOptions = {}) {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const locale = opts.locale ?? defaultLocale;\n const pieces = customTokenize(formatStr);\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore start @preserve -- unreachable: customTokenize's own\n appendLiteral() already merges adjacent literal chars/quoted\n spans into a single piece before this loop ever sees them, so\n two literal pieces are never adjacent here. Kept as a guard\n against a future customTokenize change that stops merging. */\n if (last && last.type === 'literal') last.value += piece.value;\n /* c8 ignore stop @preserve */\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable: customTokenize only emits\n kind:'token' pieces found in sortedTokenStrings, the same array\n handlerByToken is built from, so every token piece is guaranteed\n a handler entry. Guards against future decoupling of the two. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n out.push({ type: 'token', value: handler.handler(temporal, locale), token: piece.value });\n }\n return out;\n },\n compileFormat(formatStr: string): CompiledFormat {\n if (formatStr.length > MAX_FORMAT_LENGTH) {\n throw new Error(`temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`);\n }\n const pieces = customTokenize(formatStr);\n return {\n formatStr,\n pieces: pieces.map((p) => p as { kind: 'token' | 'literal'; value: string }),\n format(temporal: TemporalLike, opts: FormatOptions = {}) {\n const locale = opts.locale ?? defaultLocale;\n let result = '';\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n result += piece.value;\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the same guard's comment\n above in the un-compiled format(). */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n result += handler.handler(temporal, locale);\n }\n return result;\n },\n formatToParts(temporal: TemporalLike, opts: FormatOptions = {}) {\n const locale = opts.locale ?? defaultLocale;\n const out: FormattedPart[] = [];\n for (const piece of pieces) {\n if (piece.kind === 'literal') {\n const last = out[out.length - 1];\n /* c8 ignore start @preserve -- unreachable, see the same guard's\n comment above in the un-compiled formatToParts. */\n if (last && last.type === 'literal') last.value += piece.value;\n /* c8 ignore stop @preserve */\n else out.push({ type: 'literal', value: piece.value });\n continue;\n }\n const handler = handlerByToken.get(piece.value);\n /* c8 ignore start @preserve -- unreachable, see the same guard's\n comment above in the un-compiled formatToParts. */\n if (!handler) throw new Error(`temporal-fmt: unknown token \"${piece.value}\"`);\n /* c8 ignore stop @preserve */\n if (temporal[handler.field] === undefined) {\n throw new Error(`temporal-fmt: token \"${piece.value}\" requires \"${String(handler.field)}\".`);\n }\n out.push({ type: 'token', value: handler.handler(temporal, locale), token: piece.value });\n }\n return out;\n },\n };\n },\n };\n}\n\n","// IDE tooling data exports. Provides structured data editor plugins can\n// consume for: token autocomplete, hover docs, inline diagnostics,\n// quick fixes, format preview, token conversion hints from\n// Day.js/date-fns, documentation links.\n//\n// This is the data layer only — an actual VS Code extension is out of\n// scope for this package. A separate package can build the extension\n// on top of these exports.\n\nimport { TOKEN_METADATA, ALL_TOKEN_NAMES } from './tokenMetadata.js';\nimport { analyzeFormat, listTokens } from './analyze.js';\nimport { format as builtinFormat } from './format.js';\n\nexport interface TokenAutocompleteEntry {\n label: string;\n detail: string;\n documentation: string;\n // Sort group — same-family tokens sort together in the autocomplete list.\n family: string;\n}\n\n// Token family grouping for autocomplete organization: year, month,\n// day, etc.\nfunction tokenFamily(name: string): string {\n if (/^y+$/.test(name)) return 'Year';\n if (/^[ML]+$/.test(name)) return 'Month';\n if (/^[decD]+$/.test(name)) return 'Day';\n if (/^[Ec]+$/.test(name)) return 'Weekday';\n if (/^[Qq]+$/.test(name)) return 'Quarter';\n if (/^[Gg]+$/.test(name)) return 'Era';\n if (/^[Hh]+$/.test(name)) return 'Hour';\n if (/^m+$/.test(name)) return 'Minute';\n if (/^s+$/.test(name)) return 'Second';\n if (/^S+$/.test(name)) return 'Fractional Second';\n if (name === 'a') return 'Day Period';\n if (name === 'zzz') return 'Time Zone';\n if (/^[Xx]+$/.test(name)) return 'UTC Offset';\n if (name === 'do') return 'Ordinal Day';\n if (name === 'ww' || name === 'RRRR') return 'ISO Week';\n return 'Other';\n}\n\nexport function getAutocompleteData(): TokenAutocompleteEntry[] {\n return ALL_TOKEN_NAMES.map((name) => {\n const meta = TOKEN_METADATA[name]!;\n return {\n label: name,\n detail: meta.meaning,\n documentation: [\n meta.meaning,\n ``,\n `Format-capable: ${meta.formatCapable}`,\n `Parse-capable: ${meta.parseCapable}`,\n `Locale-sensitive: ${meta.localeSensitive}`,\n `Calendar-sensitive: ${meta.calendarSensitive}`,\n `Timezone-sensitive: ${meta.timezoneSensitive}`,\n `Round-trip safe: ${meta.roundTripSafe}`,\n `Supported types: ${meta.supportedTypes.join(', ')}`,\n ].join('\\n'),\n family: tokenFamily(name),\n };\n });\n}\n\nexport interface TokenHoverDoc {\n signature: string;\n summary: string;\n details: string;\n examples?: string[];\n}\n\nexport function getHoverDocs(): Record<string, TokenHoverDoc> {\n const result: Record<string, TokenHoverDoc> = {};\n for (const name of ALL_TOKEN_NAMES) {\n const meta = TOKEN_METADATA[name]!;\n result[name] = {\n signature: name,\n summary: meta.meaning,\n details: [\n // every entry in TOKEN_METADATA currently has formatCapable:\n // true (verified: 51/51). The 'no' branch reflects real,\n // intended behavior for a future format-incapable token —\n // there's no such token today, so this isn't reachable data,\n // not dead logic. Every other field on this line has real\n // true/false variance across the token table and is exercised\n // by this same loop running over ALL_TOKEN_NAMES.\n /* c8 ignore next */\n `Format-capable: ${meta.formatCapable ? 'yes' : 'no'}`,\n `Parse-capable: ${meta.parseCapable ? 'yes' : 'no'}`,\n `Locale-sensitive: ${meta.localeSensitive ? 'yes' : 'no'}`,\n `Calendar-sensitive: ${meta.calendarSensitive ? 'yes' : 'no'}`,\n `Timezone-sensitive: ${meta.timezoneSensitive ? 'yes' : 'no'}`,\n `Round-trip safe: ${meta.roundTripSafe ? 'yes' : 'no'}`,\n `Supported types: ${meta.supportedTypes.join(', ')}`,\n ].join('\\n'),\n };\n }\n return result;\n}\n\n// Inline diagnostic data for a format string. Returns a list of issues\n// (warnings + errors) with position + message + suggested fix where\n// available. Mirrors analyzeFormat's warnings but adds the position\n// info an editor needs to draw squigglies.\nexport interface InlineDiagnostic {\n startColumn: number;\n endColumn: number;\n severity: 'error' | 'warning' | 'info';\n message: string;\n code: string;\n // Suggested replacement (for quick-fix). Empty when no quick fix exists.\n suggestion?: string;\n}\n\nexport function getInlineDiagnostics(formatStr: string): InlineDiagnostic[] {\n const analysis = analyzeFormat(formatStr);\n const diagnostics: InlineDiagnostic[] = [];\n for (const warning of analysis.warnings) {\n // Find the token that triggered the warning. analyzeFormat doesn't\n // currently surface which token caused which warning — we'd need\n // to extend it to track per-warning source positions. For now,\n // surface as a whole-string diagnostic.\n let message = warning.message;\n let suggestion: string | undefined;\n if (warning.code === 'TWELVE_HOUR_WITHOUT_A') {\n suggestion = 'Add an \"a\" token (e.g. \"h:mm a\") or use \"H:mm\" for 24-hour.';\n } else if (warning.code === 'MIXED_12_AND_24_HOUR') {\n suggestion = 'Pick one form: \"HH:mm\" for 24-hour or \"h:mm a\" for 12-hour.';\n } else if (warning.code === 'AMBIGUOUS_NUMERIC_RUN') {\n suggestion = 'Add a separator or use padded forms (e.g. \"MM\" instead of \"M\").';\n } else if (warning.code === 'FORMAT_ONLY_TOKEN') {\n suggestion = 'Use a parse-capable variant (e.g. \"d\" instead of \"do\").';\n }\n diagnostics.push({\n startColumn: 0,\n endColumn: formatStr.length,\n severity: 'warning',\n message,\n code: warning.code,\n suggestion,\n });\n }\n return diagnostics;\n}\n\n// Day.js / date-fns token conversion hints. Used by the IDE to suggest\n// the temporal-fmt equivalent when a user types a Day.js or date-fns\n// token. Pulls the translation tables from the codemod if available;\n// falls back to inline tables otherwise.\nexport interface TokenConversionHint {\n from: string;\n // null means \"real token in the source library, no temporal-fmt\n // equivalent\" — the codemod throws on these rather than treating\n // them as unrecognized text (see codemod.ts).\n to: string | null;\n notes?: string;\n}\n\n// Day.js → temporal-fmt mappings. Day.js uses Moment-style tokens:\n// `D`/`DD` are day-of-*month* here (unlike date-fns below, where the\n// same letters mean day-of-year). Assumes the AdvancedFormat plugin\n// for `Do`/`Q`/`k`/`kk`/`X`/`x` — see codemod.ts's doc comment for why.\nexport const DAYJS_TO_TEMPORAL_FMT: TokenConversionHint[] = [\n { from: 'YYYY', to: 'yyyy' },\n { from: 'YY', to: 'yy' },\n { from: 'MMMM', to: 'MMMM' },\n { from: 'MMM', to: 'MMM' },\n { from: 'MM', to: 'MM' },\n { from: 'M', to: 'M' },\n { from: 'DD', to: 'dd' },\n { from: 'D', to: 'd' },\n { from: 'Do', to: null, notes: 'Ordinal day (AdvancedFormat) — use temporal-fmt\\'s own \"do\" token instead.' },\n { from: 'dddd', to: 'EEEE' },\n { from: 'ddd', to: 'EEE' },\n { from: 'dd', to: null, notes: 'Min-name weekday (e.g. \"Tu\") — temporal-fmt has no equivalent width.' },\n { from: 'd', to: null, notes: 'Numeric weekday (0-6) — no temporal-fmt equivalent; not the same as \"d\" here, which is day-of-month.' },\n { from: 'HH', to: 'HH' },\n { from: 'H', to: 'H' },\n { from: 'hh', to: 'hh' },\n { from: 'h', to: 'h' },\n { from: 'kk', to: null, notes: 'Hour 1-24 (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'k', to: null, notes: 'Hour 1-24 (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'mm', to: 'mm' },\n { from: 'm', to: 'm' },\n { from: 'ss', to: 'ss' },\n { from: 's', to: 's' },\n { from: 'SSS', to: 'SSS' },\n { from: 'A', to: 'a', notes: 'Uppercase AM/PM in Day.js — temporal-fmt is always lowercase.' },\n { from: 'a', to: 'a' },\n { from: 'ZZ', to: 'XX', notes: 'Numeric UTC offset, no colon.' },\n { from: 'Z', to: 'XXX', notes: 'Numeric UTC offset with colon.' },\n { from: 'X', to: null, notes: 'Unix timestamp (seconds) — use fromUnixSeconds() instead.' },\n { from: 'x', to: null, notes: 'Unix timestamp (ms) — use fromUnixMilliseconds() instead.' },\n { from: 'Qo', to: null, notes: 'Ordinal quarter (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'Q', to: 'Q' },\n { from: 'Mo', to: null, notes: 'Ordinal month (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'ww', to: 'ww' },\n { from: 'wo', to: null, notes: 'Ordinal ISO week (AdvancedFormat) — no temporal-fmt equivalent.' },\n { from: 'w', to: null, notes: 'Unpadded ISO week — temporal-fmt\\'s \"ww\" is always 2-digit.' },\n { from: 'gggg', to: 'RRRR', notes: 'Week-numbering year, not the calendar year — same caveat as temporal-fmt\\'s RRRR.' },\n { from: 'L', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LLL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LLLL', to: null, notes: 'Localized date format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LT', to: null, notes: 'Localized time format (AdvancedFormat) — write the format string out explicitly.' },\n { from: 'LTS', to: null, notes: 'Localized time format (AdvancedFormat) — write the format string out explicitly.' },\n];\n\n// date-fns → temporal-fmt mappings. date-fns already uses Unicode/LDML\n// -style tokens close to temporal-fmt's own vocabulary — most strings\n// pass through unchanged. The catch: `D`/`DD` here mean day-of-*year*\n// (same as temporal-fmt's `D`/`DD`), not day-of-month like Day.js's\n// `D`/`DD` above — don't reuse the Day.js table for this, they collide.\nexport const DATE_FNS_TO_TEMPORAL_FMT: TokenConversionHint[] = [\n { from: 'yyyy', to: 'yyyy' },\n { from: 'yy', to: 'yy' },\n { from: 'y', to: null, notes: 'Unpadded calendar year, opt-in via useAdditionalWeekYearTokens — temporal-fmt has no unpadded-year token; use \"yyyy\".' },\n { from: 'MMMM', to: 'MMMM' },\n { from: 'MMM', to: 'MMM' },\n { from: 'MM', to: 'MM' },\n { from: 'M', to: 'M' },\n { from: 'LLLL', to: 'LLLL' },\n { from: 'LLL', to: 'LLL' },\n { from: 'dd', to: 'dd' },\n { from: 'd', to: 'd' },\n { from: 'do', to: 'do' },\n { from: 'DDD', to: 'DDD' },\n { from: 'DD', to: 'DD' },\n { from: 'D', to: 'D', notes: 'Day-of-year, opt-in via useAdditionalDayOfYearTokens — matches temporal-fmt\\'s own \"D\" already.' },\n { from: 'EEEE', to: 'EEEE' },\n { from: 'EEE', to: 'EEE' },\n { from: 'eeee', to: null, notes: 'Locale-aware numeric weekday — no temporal-fmt equivalent.' },\n { from: 'cccc', to: 'cccc' },\n { from: 'ccc', to: 'ccc' },\n { from: 'HH', to: 'HH' },\n { from: 'H', to: 'H' },\n { from: 'hh', to: 'hh' },\n { from: 'h', to: 'h' },\n { from: 'mm', to: 'mm' },\n { from: 'm', to: 'm' },\n { from: 'ss', to: 'ss' },\n { from: 's', to: 's' },\n { from: 'SSS', to: 'SSS' },\n { from: 'a', to: 'a' },\n { from: 'aaa', to: 'a' },\n { from: 'XXX', to: 'XXX' },\n { from: 'XX', to: 'XX' },\n { from: 'X', to: 'X', notes: 'Opt-in via useAdditionalDayOfYearTokens-adjacent rules in date-fns v3+; matches temporal-fmt\\'s own \"X\" already.' },\n { from: 'xxx', to: 'xxx' },\n { from: 'xx', to: 'xx' },\n { from: 'x', to: 'x' },\n { from: 'zzzz', to: 'zzzz' },\n { from: 'zzz', to: 'zzz' },\n { from: 'z', to: 'z' },\n { from: 'QQQ', to: 'QQQ' },\n { from: 'Q', to: 'Q' },\n { from: 'GGGG', to: 'GGGG' },\n { from: 'GGG', to: null, notes: 'Abbreviated era — temporal-fmt only has \"G\" (short) and \"GGGG\" (long).' },\n { from: 'GG', to: null, notes: 'Abbreviated era — temporal-fmt only has \"G\" (short) and \"GGGG\" (long).' },\n { from: 'G', to: 'G' },\n { from: 'ww', to: 'ww' },\n { from: 'w', to: null, notes: 'Unpadded local week number — temporal-fmt\\'s \"ww\" is ISO-week and always 2-digit.' },\n { from: 'RRRR', to: 'RRRR' },\n { from: 'R', to: null, notes: 'Unpadded ISO week-numbering year — temporal-fmt\\'s \"RRRR\" is always 4-digit.' },\n { from: 'Y', to: null, notes: 'Week-numbering year (locale week rules), opt-in — no temporal-fmt equivalent; \"RRRR\" is ISO week-numbering, a different rule set.' },\n { from: 'PPPP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'PPP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'PP', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'P', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'pppp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'ppp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'pp', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n { from: 'p', to: null, notes: 'Localized composite format — write the format string out explicitly.' },\n];\n\n// Format preview — for a given format string and a sample Temporal\n// value, returns the formatted string. Editor plugins use this for\n// live preview as the user types.\nexport function previewFormat(formatStr: string, sample?: unknown): string {\n // Use a fixed sample date if none provided — 2026-08-04T15:45:30.\n // Picked because it exercises every common token: weekday is Tuesday,\n // month name has 5 chars, day is single-digit, hour is 12-hour-style,\n // minute/second both pad.\n const value = sample ?? {\n year: 2026, month: 8, day: 4, hour: 15, minute: 45, second: 30, millisecond: 123,\n dayOfWeek: 2, timeZoneId: 'UTC', offset: '+00:00', calendarId: 'iso8601',\n };\n return builtinFormat(value as Parameters<typeof builtinFormat>[0], formatStr, { locale: 'en-US' });\n}\n\n// Documentation links. Docs used to live under docs/ as one file per\n// token family; they're now consolidated into the root README's \"Token\n// reference\" section, which has no per-token anchors — so this points\n// at the section instead of a token-specific fragment.\nexport function getDocUrl(tokenName: string): string {\n void tokenName;\n return 'README.md#token-reference';\n}\n\n// Re-exports for callers that want everything IDE-related from one module.\nexport { listTokens, TOKEN_METADATA, ALL_TOKEN_NAMES };","// Day.js / date-fns format-string translation — backs the CLI's\n// `translate` subcommand. The mapping tables already live in\n// ideData.ts for the IDE tooling, so this module builds the actual\n// string translator on top of them rather than depending on anything\n// external.\n//\n// Scope: format *strings* only (`\"YYYY-MM-DD\"` -> `\"yyyy-MM-dd\"`), not\n// an AST codemod that rewrites call sites. Rewriting\n// `dayjs(x).format(fmt)` call expressions across a codebase is a\n// separate, bigger job — see `temporal-fmt-codemod` for that.\n\nimport { DAYJS_TO_TEMPORAL_FMT, DATE_FNS_TO_TEMPORAL_FMT, type TokenConversionHint } from './ideData.js';\n\ninterface SourcePiece {\n kind: 'token' | 'literal';\n value: string;\n}\n\n// Day.js and date-fns both escape literal text by wrapping it in\n// square brackets (`[MM]` stays literal \"MM\"), unlike temporal-fmt's\n// single-quote escaping. Splitting on that first keeps the token scan\n// below from matching inside an escaped span.\nfunction splitOnBrackets(source: string): SourcePiece[] {\n const pieces: SourcePiece[] = [];\n let i = 0;\n while (i < source.length) {\n if (source[i] === '[') {\n const end = source.indexOf(']', i + 1);\n if (end === -1) {\n pieces.push({ kind: 'token', value: source.slice(i) });\n break;\n }\n pieces.push({ kind: 'literal', value: source.slice(i + 1, end) });\n i = end + 1;\n } else {\n let j = i;\n while (j < source.length && source[j] !== '[') j += 1;\n pieces.push({ kind: 'token', value: source.slice(i, j) });\n i = j;\n }\n }\n return pieces;\n}\n\n// Splits a \"token\" span (the non-bracketed parts of the source string)\n// into runs of identical letters and everything else, greedy-longest\n// against the known token table first — same overall approach as\n// tokenize.ts, but here we're scanning the *source* library's\n// vocabulary rather than temporal-fmt's own.\nfunction splitIntoRuns(span: string, sortedFrom: string[]): SourcePiece[] {\n const pieces: SourcePiece[] = [];\n let i = 0;\n while (i < span.length) {\n const match = sortedFrom.find((tok) => span.startsWith(tok, i));\n if (match) {\n pieces.push({ kind: 'token', value: match });\n i += match.length;\n continue;\n }\n const last = pieces[pieces.length - 1];\n if (last && last.kind === 'literal') {\n last.value += span[i];\n } else {\n pieces.push({ kind: 'literal', value: span[i] });\n }\n i += 1;\n }\n return pieces;\n}\n\nfunction translate(source: string, table: TokenConversionHint[], sourceLibLabel: string): string {\n const byFrom = new Map(table.map((hint) => [hint.from, hint.to]));\n const sortedFrom = [...byFrom.keys()].sort((a, b) => b.length - a.length);\n\n const bracketPieces = splitOnBrackets(source);\n let out = '';\n for (const piece of bracketPieces) {\n if (piece.kind === 'literal') {\n // Re-escape for temporal-fmt's quote syntax; a literal quote\n // character inside the escaped span needs doubling.\n out += `'${piece.value.replace(/'/g, \"''\")}'`;\n continue;\n }\n for (const run of splitIntoRuns(piece.value, sortedFrom)) {\n if (run.kind === 'token') {\n const mapped = byFrom.get(run.value);\n if (mapped === undefined || mapped === null) {\n throw new Error(\n `temporal-fmt: \"${run.value}\" has no ${sourceLibLabel} -> temporal-fmt mapping ` +\n `(in format string \"${source}\"). Write the equivalent temporal-fmt token in by hand, ` +\n `or wrap it in brackets if it was meant as literal text.`\n );\n }\n out += mapped;\n } else if (/[a-zA-Z]/.test(run.value)) {\n // Unrecognized letters can't pass through unescaped — temporal-fmt\n // would read them as its own tokens. Quote the whole run.\n out += `'${run.value.replace(/'/g, \"''\")}'`;\n } else {\n out += run.value;\n }\n }\n }\n return out;\n}\n\n/**\n * Translates a Day.js format string to the equivalent temporal-fmt\n * token string.\n *\n * ```js\n * translateDayjsFormatString('YYYY-MM-DD HH:mm:ss'); // \"yyyy-MM-dd HH:mm:ss\"\n * translateDayjsFormatString('[Q]Q YYYY'); // \"'Q'Q yyyy\" (bracketed text stays literal)\n * ```\n *\n * Throws if the format string uses a Day.js token with no temporal-fmt\n * equivalent (`Do`, `Mo`, `Qo`, `k`, `kk`, `X`, `x`, the localized\n * `L`-family tokens) — these need to be rewritten by hand, since\n * there's no single temporal-fmt token that means the same thing.\n *\n * Note: `Do`/`Mo`/`Qo`/`k`/`kk`/`X`/`x` only have meaning in Day.js\n * once the `AdvancedFormat` (and, for `X`/`x`, base) plugins are\n * loaded — without them Day.js silently glues the parts together or\n * passes the letters through unrendered instead of erroring. This\n * function assumes the plugin is loaded, since that's the common case\n * for anyone who put these tokens in a format string on purpose.\n */\nexport function translateDayjsFormatString(formatStr: string): string {\n return translate(formatStr, DAYJS_TO_TEMPORAL_FMT, 'Day.js');\n}\n\n/**\n * Translates a date-fns format string to the equivalent temporal-fmt\n * token string. date-fns already uses Unicode-style tokens close to\n * temporal-fmt's own, so most strings pass through with only a case\n * change on the weekday tokens; the notable divergences are date-fns's\n * `D`/`DD` (day-of-year, matches temporal-fmt already) versus its `d`/\n * `dd` (day-of-month, also matches already) and the locale-dependent\n * `P`/`p` composite tokens, which have no single temporal-fmt token\n * and throw.\n */\nexport function translateDateFnsFormatString(formatStr: string): string {\n return translate(formatStr, DATE_FNS_TO_TEMPORAL_FMT, 'date-fns');\n}\n"],"mappings":"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","names":["index_exports","__export","ALL_TOKEN_NAMES","AmbiguousInputError","DATE_FNS_TO_TEMPORAL_FMT","DAYJS_TO_TEMPORAL_FMT","DEFAULT_CONFIG","FORMAT_ONLY_TOKENS","FormatSyntaxError","InvalidCalendarError","InvalidDateError","InvalidDurationError","InvalidLocaleError","InvalidOffsetError","InvalidTimeError","InvalidTimeZoneError","ParseMismatchError","SUPPORTED_NUMBERING_SYSTEMS","TOKEN_METADATA","TemporalFmtError","UnknownTokenError","add","addBusinessDays","addDays","addDuration","addHours","addMilliseconds","addMinutes","addMonths","addSeconds","addWeeks","addYears","analyzeFormat","applyNumbering","applyParseNumbering","asDateFieldView","assertDuration","assertInstant","assertPlainDate","assertPlainDateTime","assertPlainMonthDay","assertPlainTime","assertPlainYearMonth","assertTemporal","assertZonedDateTime","balanceDuration","between","ceil","clamp","compare","compareDuration","compileFormat","compileParser","convertDigits","convertDigitsToAscii","createBusinessCalendar","createConfig","createFormatter","createHolidayCalendar","dayOfYearHelper","daysInMonth","daysInYear","difference","differenceInBusinessDays","differenceInDays","differenceInHours","differenceInMilliseconds","differenceInMinutes","differenceInMonths","differenceInSeconds","differenceInWeeks","differenceInYears","endOf","explainFormat","fieldForToken","floor","format","formatDistance","formatDistanceToNow","formatDuration","formatDurationToParts","formatHTTPDate","formatISO","formatISODuration","formatRFC2822","formatRFC3339","formatRRule","formatRange","formatRangeToParts","formatRelative","formatRelativeToNow","formatSQL","formatToParts","fromUnixMicroseconds","fromUnixMilliseconds","fromUnixNanoseconds","fromUnixSeconds","getAutocompleteData","getDocUrl","getHoverDocs","getInlineDiagnostics","getLocale","getMonth","getNextTransition","getOffset","getOffsetNanoseconds","getPreviousTransition","getQuarter","getTimeZone","getTransitions","getWeekday","hasLocale","holidaysBetween","intersection","intersects","interval","contains","isAfter","isBefore","subtract","isBetween","isBusinessDay","isDST","isDuration","isEqual","isInstant","isLeapMonth","isLeapYear","isPlainDate","isPlainDateTime","isPlainMonthDay","isPlainTime","isPlainYearMonth","isSameDay","isSameMonth","isSameQuarter","isSameWeek","isSameYear","isTemporal","isToday","isTomorrow","isValidFormat","isWeekday","isWeekend","isYesterday","isZonedDateTime","listRegisteredGrammars","listTokens","max","mergeIntervals","mergeWithConfig","min","monthsInYear","nextBusinessDay","nextHoliday","overlaps","parse","parseDuration","parseHTTPDate","parseISO","parseISODuration","parseRFC2822","parseRFC3339","parseRRule","parseRelative","parseSQL","parseToParts","possibleInstantsFor","previewFormat","previousBusinessDay","previousHoliday","recurrence","registerLocale","registerLocaleVocab","registerRelativeGrammar","resolveZoned","round","roundDuration","safeParse","setTemporal","skip","splitInterval","startOf","subtractBusinessDays","subtractDays","subtractDuration","subtractHours","subtractMilliseconds","subtractMinutes","subtractMonths","subtractSeconds","subtractWeeks","subtractYears","take","toUnixMicroseconds","toUnixMilliseconds","toUnixNanoseconds","toUnixSeconds","tokenInfo","tokenizeFormat","totalDuration","translateDateFnsFormatString","translateDayjsFormatString","truncate","tryParse","union","validateFormat","weekOfYear","weekYear","__toCommonJS","injectedTemporal","onTemporalChanged","subscribeToTemporalChanges","listener","setTemporal","temporal","resolveTemporal","getTemporal","TemporalFmtError","message","fields","capture","FormatSyntaxError","rest","UnknownTokenError","ParseMismatchError","InvalidDateError","InvalidTimeError","InvalidOffsetError","InvalidTimeZoneError","InvalidCalendarError","AmbiguousInputError","InvalidLocaleError","InvalidDurationError","wrapUntypedError","err","context","msg","lowerMsg","customVocabs","MAX_CUSTOM_VOCABS","MAX_LOCALE_TAG_LENGTH","MAX_VOCAB_ENTRY_LENGTH","assertValidVocab","vocab","locale","required","key","length","label","value","entry","assertNoCollision","onVocabChanged","subscribeToVocabChanges","listener","registerLocaleVocab","cacheKey","canonicalCacheKey","vocabCache","normalizeLocaleTag","canonicalKeyCache","MAX_CANONICAL_KEY_CACHE","hit","oldestKey","assertValidLocaleTag","InvalidLocaleError","MAX_VOCAB_CACHE_SIZE","partValue","formatter","date","type","parts","index","p","prev","next","names","seen","i","prior","getCustomVocab","getLocaleVocab","custom","cached","intlLocale","monthLongFmt","monthShortFmt","monthLong","monthShort","m","weekdayLongFmt","weekdayShortFmt","weekdayLong","weekdayShort","d","dayPeriodFmt","am","pm","dayPeriod","CUMULATIVE_DAYS_BY_MONTH","isGregorianLeapYear","year","daysInYear","dayOfYear","month","day","doy","REFERENCE_YEAR","dayOfWeekOfJan1","year","offset","REFERENCE_YEAR","y","daysInYear","isoWeekYearAndWeek","month","day","dayOfWeek","thursdayDoyRelative","dayOfYear","isoYear","thursdayDoy","firstThursdayDoy","week","pad","n","len","negative","digits","t","width","nanoOfSecond","DEFAULT_LOCALE","formatterCache","MAX_CACHE_SIZE","getFormatter","locale","options","key","canonicalCacheKey","formatter","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","nativeSupport","subscribeToTemporalChanges","intlSupportsNativeTemporal","temporal","getTemporal","intlPart","partType","calendar","formatterOptions","ls","toInstant","timeZoneId","isZoned","intlSafeTemporal","nativeOptions","parts","index","p","value","prev","next","dayPeriodPart","hour","custom","getCustomVocab","date","part","localeAwareName","customArray","customIndex","formatOffset","offset","variant","sign","hours","minutes","TOKENS","day","lastDigit","lastTwoDigits","week","isoWeekYearAndWeek","isoYear","dayOfYear","SORTED_TOKEN_STRINGS","TOKENS","tok","a","b","tokenize","format","pieces","i","ch","appendLiteral","j","literal","closed","FormatSyntaxError","match","runChar","end","UnknownTokenError","value","last","SUPPORTED_NUMBERING_SYSTEMS","digitMapCache","getDigitMap","system","map","i","fmt","convertDigits","s","InvalidLocaleError","result","ch","convertDigitsToAscii","reverse","k","applyNumbering","options","applyParseNumbering","HANDLER_BY_TOKEN","TOKENS","tok","fn","field","tokenizeCache","MAX_TOKENIZE_CACHE_SIZE","getPieces","formatStr","pieces","oldestKey","tokenize","format","temporal","options","FormatSyntaxError","locale","DEFAULT_LOCALE","result","piece","handler","UnknownTokenError","applyNumbering","formatToParts","last","compileFormat","out","_handlerFor","token","HANDLER_BY_TOKEN","escapeRegExp","literal","alternation","values","caseInsensitive","escaped","foldCase","value","ch","TIME_ZONE_SHAPE","OFFSET_SHAPES","getTimeZoneFragment","validZoneSet","getValidZoneSet","FIXED_OFFSET_RE","isValidTimeZone","raw","getTemporal","NUMERIC_FRAGMENTS","QQQ_FRAGMENT","FORMAT_ONLY_TOKENS","YYYY_EXACT","YYYY_EXTENDED","Y_FRAGMENT","DIGIT_LEADING_TOKENS","UNBOUNDED_WIDTH_TOKENS","tokenFragment","token","locale","nextToken","numeric","UnknownTokenError","vocab","getLocaleVocab","UNPADDED_NUMERIC_TOKENS","UNPADDED_NUMERIC_RANGES","enumerateValidSplits","digits","tokens","memo","solve","tokenIndex","offset","key","cached","result","ranges","results","width","min","max","piece","restSplit","escapeRegExp","literal","MAX_AMBIGUITY_BITS","widthChoicesBits","choices","isDigitConsumingStart","piece","DIGIT_LEADING_TOKENS","buildCapturingPattern","pieces","locale","groups","ambiguousRuns","source","i","ambiguityBits","currentRun","flushRun","nextPiece","name","token","tokenFragment","runName","tokenCount","idx","UNPADDED_NUMERIC_TOKENS","nextToken","UNBOUNDED_WIDTH_TOKENS","FormatSyntaxError","patternCache","MAX_CACHE_SIZE","subscribeToVocabChanges","getPattern","formatStr","locale","key","canonicalCacheKey","pattern","oldestKey","buildCapturingPattern","tokenize","calendarCache","MAX_CALENDAR_CACHE_SIZE","resolveCalendar","assertValidLocaleTag","canonicalLocale","calendar","parts","extensionIndex","calendarKeyIndex","resolved","parseOffsetString","raw","token","sign","body","hoursStr","minutesStr","hours","minutes","InvalidOffsetError","assignField","fields","value","applyGroup","vocab","getLocaleVocab","nanoOfSecond","periodIndex","p","pickLenientSplit","splits","tokens","dayIndex","smallDaySplits","s","resolveYear","FormatSyntaxError","resolveHour","parse","input","options","applyParseNumbering","DEFAULT_LOCALE","match","ParseMismatchError","name","isValidTimeZone","InvalidTimeZoneError","runValues","run","runDigits","enumerateValidSplits","AmbiguousInputError","picked","idx","year","hour","month","day","minute","second","millisecond","microsecond","nanosecond","timeZoneId","offsetString","weekdayExpected","weekdayRaw","quarter","hasAnyDatePart","hasFullDate","InvalidDateError","hasTime","temporal","getTemporal","timeFields","calendarField","reject","result","zoneOptions","zdt","actualOffset","err","actual","expectedQuarter","safeParse","TemporalFmtError","wrapUntypedError","tryParse","parseToParts","groupIndices","consumed","fromIndices","position","compileParser","opts","DURATION_UNITS","DURATION_TOKEN_STRINGS","single","a","b","tokenizeDuration","format","pieces","i","ch","appendLiteral","j","literal","closed","match","tok","unit","form","value","last","readUnit","duration","field","coerced","unitFormatterCache","MAX_UNIT_FORMATTER_CACHE_SIZE","getUnitFormatter","locale","intlUnit","unitDisplay","key","canonicalCacheKey","cached","oldestKey","formatter","normalizeLocaleTag","err","InvalidLocaleError","formatDuration","formatStr","options","renderDurationPieces","p","showZeroes","useIntl","out","piece","readFields","value","label","obj","hasYear","hasMonth","hasDay","REFERENCE_YEAR","MS_PER_SECOND","MS_PER_MINUTE","MS_PER_HOUR","MS_PER_DAY","daysSinceReference","year","month","day","days","y","isGregorianLeapYear","dayOfYear","toEpochMs","fields","hour","minute","second","millisecond","DEFAULT_CUTOFFS","resolveCutoffs","user","merged","key","secondsMs","minutesMs","hoursMs","daysMs","monthsMs","rtfCache","MAX_RTF_CACHE_SIZE","getRtf","locale","numeric","canonicalLocaleKey","rtf","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","formatDistance","date1","date2","options","fields1","fields2","diffMs","absMs","unitCutoffs","unit","maxMs","candidateUnit","divisor","unitToMs","rounded","DEFAULT_LOCALE","formatDistanceToNow","date","now","nowFields","MAX_REGISTERED_GRAMMARS","registeredGrammars","registerRelativeGrammar","grammar","existingIdx","g","tryRegisteredGrammar","language","input","reference","matcher","match","temporal","getTemporal","fields","listRegisteredGrammars","normalizeForMatch","s","levenshtein","a","b","prev","curr","j","i","cost","FUZZY_MAX_DISTANCE","MAX_RELATIVE_INPUT_LENGTH","MAX_RELATIVE_WORDS","buildEnglishFuzzyVocabulary","ENGLISH_WEEKDAYS","ENGLISH_MONTHS","ENGLISH_MONTH_SHORT","englishFuzzyVocabulary","getEnglishFuzzyVocabulary","fuzzyCorrectEnglish","normalized","vocabulary","words","changed","corrected","word","best","bestDist","candidate","dist","readYear","reference","readMonth","readDay","extractDayOfWeek","dow","nameAlternation","names","n","buildPlainDate","temporal","year","month","day","addDays","days","addUnits","count","unit","refDate","duration","weekdayOffset","rel","refDow","targetDow","diff","resolveToNextOccurrence","refYear","thisYear","err","weekdayIndexFromName","name","idx","monthIndexFromName","longNames","shortNames","longIdx","shortIdx","ENGLISH_GRAMMAR","_m","ctx","m","inPrefix","unitName","hasIn","hasAgo","sign","SPANISH_WEEKDAYS","SPANISH_MONTHS","SPANISH_MONTH_SHORT","SPANISH_GRAMMAR","relWord","weekdayName","unitWord","unitWordToEnglish","_ctx","FRENCH_WEEKDAYS","FRENCH_MONTHS","FRENCH_MONTH_SHORT","FRENCH_GRAMMAR","GERMAN_WEEKDAYS","GERMAN_MONTHS","GERMAN_MONTH_SHORT","GERMAN_GRAMMAR","adj","GRAMMARS","grammarForLocale","locale","lang","languageOf","w","parseRelative","input","referenceDate","options","reference","temporal","getTemporal","trimmed","MAX_RELATIVE_INPUT_LENGTH","normalized","normalizeForMatch","grammar","grammarForLocale","ctx","registered","tryRegisteredGrammar","languageOf","tryMatch","text","matcher","match","NO_MATCH","exactResult","ENGLISH_GRAMMAR","corrected","fuzzyCorrectEnglish","fuzzyResult","isObject","v","guardOrFalse","impl","value","hasMethod","name","hasTag","typeName","isPlainDate","isPlainTime","isPlainDateTime","isZonedDateTime","isInstant","isPlainYearMonth","isPlainMonthDay","isDuration","isTemporal","tag","describeValue","ctor","assertImpl","guard","expectedType","assertTemporal","assertInstant","assertPlainDate","assertPlainTime","assertPlainDateTime","assertZonedDateTime","assertPlainYearMonth","assertPlainMonthDay","assertDuration","TYPES_WITH","TOKEN_METADATA","ALL_TOKEN_NAMES","tokenizeFormat","formatStr","tokenize","listTokens","TOKENS","name","TOKEN_METADATA","tokenInfo","analyzeFormat","pieces","tokens","requiredFields","compatibleTypesSet","parseable","localeSensitive","calendarSensitive","timezoneSensitive","roundTripSafe","warnings","offset","unpaddedRun","flushRun","piece","metadata","handler","_handlerFor","t","stillCompatible","UNPADDED_NUMERIC_TOKENS","tokenNames","has12Hour","has24Hour","hasAPeriod","hasOffset","hasZzz","w","explainFormat","analysis","lines","isValidFormat","validateFormat","fieldForToken","token","requireFields","view","fields","f","daysInMonth","year","month","LENGTHS","isGregorianLeapYear","daysInYear","monthsInYear","_view","isLeapYear","isLeapMonth","dayOfYearHelper","dayOfYear","weekOfYear","isoWeekYearAndWeek","weekYear","validateStartMonth","startMonth","getQuarter","options","shifted","getMonth","getWeekday","touchesTime","unit","recomputeDayOfWeek","jsDow","startOf","value","result","asDateFieldView","endOf","v","out","asDateTime","value","asDateFieldView","recomputeDayOfWeek","view","jsDow","add","amount","unit","result","maxDay","daysInMonth","total","shiftDays","shiftTime","v","days","y","m","d","y2","era","yoe","m2","doy","doe","z","era2","doe2","yoe2","y2out","doy2","mp","d2","m2out","yOut","msDelta","totalMs","dayOverflow","withinDay","hour","minute","second","millisecond","subtract","addYears","addMonths","addWeeks","addDays","addHours","addMinutes","addSeconds","addMilliseconds","subtractYears","subtractMonths","subtractWeeks","subtractDays","subtractHours","subtractMinutes","subtractSeconds","subtractMilliseconds","difference","a","b","av","bv","aDays","toDayCount","MS_PER_HOUR","MS_PER_MINUTE","MS_PER_SECOND","aMs","toTotalMs","diffMs","differenceInYears","differenceInMonths","differenceInWeeks","differenceInDays","differenceInHours","differenceInMinutes","differenceInSeconds","differenceInMilliseconds","toComparableMs","view","y","m","d","y2","era","yoe","m2","doy","doe","asDateTimeFieldView","value","asDateFieldView","compare","a","b","av","bv","am","bm","isEqual","isBefore","isAfter","min","values","best","i","max","clamp","lo","hi","isBetween","readYearMonthDay","v","todayYearMonthDay","now","dateEqualYearMonthDay","isSameDay","reference","isToday","tomorrowYearMonthDay","yesterdayYearMonthDay","isTomorrow","isYesterday","sameWeek","yearA","monthA","dayA","yearB","monthB","dayB","daysSince","weekdayOf","days","jsDow","wa","wb","isSameWeek","isSameMonth","isSameQuarter","isSameYear","isWeekend","isWeekday","MS_PER_UNIT","applyMode","ms","mode","increment","sign","stepped","rounded","toMs","v","y","m","d","y2","era","yoe","m2","doy","doe","fromMs","base","MS_PER_DAY","totalDays","withinDay","hour","minute","second","millisecond","z","mp","year","round","value","options","base","asDateFieldView","mode","increment","MS_PER_UNIT","ms","toMs","rounded","applyMode","fromMs","floor","unit","roundingIncrement","ceil","truncate","fieldToNs","v","nsPer","field","scaled","InvalidDurationError","DURATION_UNIT_TO_NS","isCalendarBound","roundDuration","duration","totalNs","u","stepNs","sign","abs","stepped","remainder","newTotalNs","result","remaining","unitsInOrder","startIdx","i","unitNs","count","parseISO","input","temporal","getTemporal","iso","offsetMatch","err","InvalidDateError","formatISO","value","v","FormatSyntaxError","parseRFC3339","formatRFC3339","s","parseRFC2822","ms","requireInstant","formatRFC2822","inst","formatRFC2822FromMs","parts","get","t","p","tzMatch","tz","sign","hours","minutes","hour","d","days","months","wd","dd","mon","yyyy","hh","mm","ss","parseHTTPDate","trimmed","formatHTTPDate","instant","getInstant","toUnixSeconds","fromUnixSeconds","seconds","fromUnixMilliseconds","fromUnixMicroseconds","µs","fromUnixNanoseconds","ns","toUnixMilliseconds","toUnixMicroseconds","epochNanoseconds","toUnixNanoseconds","parseSQL","parse","formatSQL","format","formatDurationToParts","duration","formatStr","options","renderDurationPieces","p","parseISODuration","input","m","InvalidDurationError","g","toNum","s","formatISODuration","parts","years","months","weeks","days","hours","minutes","seconds","tokenizeDurationFormat","DURATION_TOKEN_STRINGS","single","a","b","pieces","i","ch","appendLiteral","j","literal","closed","FormatSyntaxError","match","tok","value","last","parseDuration","input","formatStr","options","tokens","tokenizeDurationFormat","fields","groupSpecs","regex","tok","unit","form","key","FormatSyntaxError","m","spec","i","raw","value","InvalidDurationError","balanceDuration","duration","NS_PER","totalNs","k","nsPer","v","fieldToNs","result","remaining","count","totalDuration","divisor","compareDuration","a","b","aNs","totalDurationNs","bNs","d","total","addDuration","keys","subtractDuration","negateDuration","rtfCache","MAX_RTF_CACHE_SIZE","getRtf","locale","numeric","key","rtf","oldestKey","normalizeLocaleTag","err","InvalidLocaleError","formatRelative","date1","date2","options","DEFAULT_LOCALE","dayDiff","differenceInDays","absDays","formatRelativeToNow","date","now","nowFields","extendedVocabs","MAX_LOCALE_TAG_LENGTH","MAX_VOCAB_STRING_LENGTH","canonicalKey","locale","registerLocale","vocab","InvalidLocaleError","registerLocaleVocab","validateExtended","key","checkArray","val","length","i","getLocale","ext","getLocaleVocab","hasLocale","DEFAULT_CONFIG","DEFAULT_LOCALE","createConfig","overrides","merged","mergeWithConfig","config","perCall","result","interval","start","end","bounds","compare","contains","iv","value","startCmp","endCmp","afterStart","beforeEnd","overlaps","a","b","intersects","isBefore","isAfter","intersection","startBoundsOpen","endBoundsOpen","union","difference","result","aStartIncluded","aEndIncluded","subtract","mergeIntervals","intervals","sorted","i","current","last","MAX_DATE_MS","splitInterval","startFields","asDateFieldView","endFields","startMs","toMs","endMs","step","sliceStart","sliceEnd","fromMs","v","y","m","d","y2","era","yoe","m2","doy","doe","ms","base","dayOverflow","withinDay","hour","minute","second","millisecond","newMs","formatRange","formatStr","options","startStr","format","endStr","err","fmt","normalizeLocaleTag","startDate","toJSDate","endDate","formatRangeToParts","startParts","formatToParts","endParts","recurrence","start","rule","current","count","history","MAX_HISTORY","atEnd","atStart","matches","value","v","d","compare","advance","steps","unit","add","recordHistory","nextMatch","candidate","safetyCounter","advanced","take","iter","result","i","r","MAX_SKIP_COLLECTION","skip","between","rangeStart","rangeEnd","parseRRule","input","parts","frequencies","part","eq","key","interval","wd","values","day","month","formatRRule","map","createBusinessCalendar","options","weekend","workingHours","i","isBusinessDay","cal","value","v","dow","jsDow","nextBusinessDay","candidate","add","previousBusinessDay","MAX_BUSINESS_DAY_TRAVERSAL","assertBusinessDayCount","days","compareDateFields","a","b","av","bv","ay","by","addBusinessDays","sign","absDays","subtractBusinessDays","differenceInBusinessDays","rawDirection","direction","count","traversedDays","MAX_HOLIDAY_YEAR_RANGE","createHolidayCalendar","specs","cache","forYear","year","list","s","md","value","v","h","start","end","sv","ev","result","y","date","compare","nextHoliday","cal","candidate","add","i","previousHoliday","holidaysBetween","resolveZoned","fields","timeZone","options","temporal","getTemporal","disambiguation","fromOptions","err","msg","getTimeZone","value","v","getOffset","getOffsetNanoseconds","m","isDST","getNextTransition","findTransition","getPreviousTransition","direction","candidateFields","add","startOffset","i","cf","candidateZdt","getTransitions","start","end","result","startV","endV","cursorFields","lastOffset","cursorZdt","possibleInstantsFor","earlier","later","matchesRequestedWallClock","z","createFormatter","options","mergedTokens","TOKENS","customTokenNames","custom","existingIdx","name","a","b","handlerByToken","handler","field","sortedTokenStrings","customTokenize","formatStr","pieces","i","ch","appendLiteral","j","literal","closed","match","tok","runChar","end","UnknownTokenError","value","last","defaultLocale","DEFAULT_LOCALE","temporal","opts","locale","result","piece","out","p","tokenFamily","name","getAutocompleteData","ALL_TOKEN_NAMES","meta","TOKEN_METADATA","getHoverDocs","result","getInlineDiagnostics","formatStr","analysis","analyzeFormat","diagnostics","warning","message","suggestion","DAYJS_TO_TEMPORAL_FMT","DATE_FNS_TO_TEMPORAL_FMT","previewFormat","sample","format","getDocUrl","tokenName","splitOnBrackets","source","pieces","i","end","j","splitIntoRuns","span","sortedFrom","match","tok","last","translate","table","sourceLibLabel","byFrom","hint","a","b","bracketPieces","out","piece","run","mapped","translateDayjsFormatString","formatStr","DAYJS_TO_TEMPORAL_FMT","translateDateFnsFormatString","DATE_FNS_TO_TEMPORAL_FMT"]}
|