@vielzeug/tempo 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +71 -0
  2. package/dist/_convert.cjs +2 -0
  3. package/dist/_convert.cjs.map +1 -0
  4. package/dist/_convert.d.ts +47 -0
  5. package/dist/_convert.d.ts.map +1 -0
  6. package/dist/_convert.js +26 -0
  7. package/dist/_convert.js.map +1 -0
  8. package/dist/_floor.cjs +2 -0
  9. package/dist/_floor.cjs.map +1 -0
  10. package/dist/_floor.d.ts +12 -0
  11. package/dist/_floor.d.ts.map +1 -0
  12. package/dist/_floor.js +50 -0
  13. package/dist/_floor.js.map +1 -0
  14. package/dist/_tz.cjs +2 -0
  15. package/dist/_tz.cjs.map +1 -0
  16. package/dist/_tz.d.ts +15 -0
  17. package/dist/_tz.d.ts.map +1 -0
  18. package/dist/_tz.js +42 -0
  19. package/dist/_tz.js.map +1 -0
  20. package/dist/boundary.cjs +2 -0
  21. package/dist/boundary.cjs.map +1 -0
  22. package/dist/boundary.d.ts +26 -0
  23. package/dist/boundary.d.ts.map +1 -0
  24. package/dist/boundary.js +30 -0
  25. package/dist/boundary.js.map +1 -0
  26. package/dist/classify.cjs +2 -0
  27. package/dist/classify.cjs.map +1 -0
  28. package/dist/classify.d.ts +46 -0
  29. package/dist/classify.d.ts.map +1 -0
  30. package/dist/classify.js +83 -0
  31. package/dist/classify.js.map +1 -0
  32. package/dist/compare.cjs +2 -0
  33. package/dist/compare.cjs.map +1 -0
  34. package/dist/compare.d.ts +75 -0
  35. package/dist/compare.d.ts.map +1 -0
  36. package/dist/compare.js +71 -0
  37. package/dist/compare.js.map +1 -0
  38. package/dist/core.cjs +2 -0
  39. package/dist/core.cjs.map +1 -0
  40. package/dist/core.d.ts +136 -0
  41. package/dist/core.d.ts.map +1 -0
  42. package/dist/core.js +89 -0
  43. package/dist/core.js.map +1 -0
  44. package/dist/errors.cjs +2 -0
  45. package/dist/errors.cjs.map +1 -0
  46. package/dist/errors.d.ts +21 -0
  47. package/dist/errors.d.ts.map +1 -0
  48. package/dist/errors.js +16 -0
  49. package/dist/errors.js.map +1 -0
  50. package/dist/format.cjs +2 -0
  51. package/dist/format.cjs.map +1 -0
  52. package/dist/format.d.ts +132 -0
  53. package/dist/format.d.ts.map +1 -0
  54. package/dist/format.js +182 -0
  55. package/dist/format.js.map +1 -0
  56. package/dist/index.cjs +1 -0
  57. package/dist/index.d.ts +11 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +10 -0
  60. package/dist/range.cjs +2 -0
  61. package/dist/range.cjs.map +1 -0
  62. package/dist/range.d.ts +59 -0
  63. package/dist/range.d.ts.map +1 -0
  64. package/dist/range.js +38 -0
  65. package/dist/range.js.map +1 -0
  66. package/dist/tempo.cjs +2 -0
  67. package/dist/tempo.cjs.map +1 -0
  68. package/dist/tempo.iife.js +2 -0
  69. package/dist/tempo.iife.js.map +1 -0
  70. package/dist/tempo.js +2 -0
  71. package/dist/tempo.js.map +1 -0
  72. package/dist/types.d.ts +93 -0
  73. package/dist/types.d.ts.map +1 -0
  74. package/package.json +43 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tempo.cjs","names":[],"sources":["../src/errors.ts","../src/_tz.ts","../src/_convert.ts","../src/core.ts","../src/_floor.ts","../src/boundary.ts","../src/compare.ts","../src/format.ts","../src/classify.ts","../src/range.ts"],"sourcesContent":["/** Base class for all tempo errors. Use `instanceof TempoError` to catch any tempo-originated error. */\nexport class TempoError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is TempoError {\n return err instanceof TempoError;\n }\n}\n\n/** Thrown when a date/time input string or value cannot be parsed. */\nexport class TempoInvalidInputError extends TempoError {}\n\n/** Thrown when the provided timezone identifier is unknown or invalid. */\nexport class TempoInvalidTzError extends TempoError {}\n\n/** Thrown when an operation requires a timezone but none was supplied. */\nexport class TempoMissingTzError extends TempoError {}\n\n/** Thrown when an input type is not supported by the called operation. */\nexport class TempoUnsupportedInputError extends TempoError {}\n\n// ─── Error helpers ────────────────────────────────────────────────────────────\n\ntype TempoErrorCtor = new (message: string) => TempoError;\n\nexport function fail(message: string, Class: TempoErrorCtor = TempoInvalidInputError): never {\n throw new Class(message);\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, TimeInput } from './types';\n\nimport { TempoInvalidTzError, TempoMissingTzError, fail } from './errors';\n\n// ─── Timezone validation ──────────────────────────────────────────────────────\n\nexport function validateTz(tz: string): string {\n try {\n Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(tz);\n } catch {\n fail(\n `Unknown or invalid timezone: \"${tz}\". Expected an IANA timezone name (e.g. \"America/New_York\") or UTC offset (e.g. \"+05:30\").`,\n TempoInvalidTzError,\n );\n }\n\n return tz;\n}\n\n// ─── Timezone inference ───────────────────────────────────────────────────────\n\nexport function inferTimeZone(input: TimeInput, options: { tz?: string }): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n if (!tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return validateTz(tz);\n}\n\nexport function inferSharedTimeZone(inputs: TimeInput[], options: { tz?: string }): string {\n if (options.tz) return validateTz(options.tz);\n\n let inferred: string | undefined;\n\n for (const input of inputs) {\n if (!(input instanceof Temporal.ZonedDateTime)) continue;\n\n const tz = input.timeZoneId;\n\n if (!inferred) {\n inferred = tz;\n continue;\n }\n\n if (inferred !== tz) {\n fail('Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.');\n }\n }\n\n if (!inferred)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return inferred;\n}\n\n// ─── Range normalization ──────────────────────────────────────────────────────\n\nexport function normalizeRange(start: Temporal.Instant, end: Temporal.Instant): [Temporal.Instant, Temporal.Instant] {\n return Temporal.Instant.compare(start, end) <= 0 ? [start, end] : [end, start];\n}\n\n// ─── Shared constants ─────────────────────────────────────────────────────────\n\n/** Units that require timezone-aware context for calendar-accurate operations. */\nexport const CALENDAR_UNITS = new Set<CalendarUnit>(['day', 'month', 'week', 'year']);\n\n/** Approximate millisecond constants for threshold arithmetic. */\nexport const MS_PER_MONTH = 30.4375 * 86_400_000; // 365.25 / 12 days × 86400 s × 1000\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { DateTimeDisambiguation, TimeInput } from './types';\n\nimport { validateTz } from './_tz';\nimport { TempoMissingTzError, TempoUnsupportedInputError, fail } from './errors';\n\ntype WithPrefer = { prefer?: DateTimeDisambiguation };\ntype TimeOptionsWithTz = { tz: string };\n\n// ─── Direct resolution ────────────────────────────────────────────────────────\n\n/**\n * Converts any {@link TimeInput} to an absolute `Instant`.\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n */\nexport function toInstant(input: TimeInput, options: WithPrefer & { tz?: string } = {}): Temporal.Instant {\n if (input instanceof Temporal.Instant) return input;\n\n if (input instanceof Temporal.ZonedDateTime) return input.toInstant();\n\n if (input instanceof Temporal.PlainDateTime) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input\n .toZonedDateTime(validateTz(options.tz), {\n disambiguation: options.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n })\n .toInstant();\n }\n\n if (input instanceof Temporal.PlainDate) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input.toZonedDateTime({ timeZone: validateTz(options.tz) }).toInstant();\n }\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into `options.tz` as a `ZonedDateTime`.\n *\n * When `input` is already a `ZonedDateTime`, it is **re-projected** into `options.tz`\n * via `withTimeZone()` — the wall-clock time changes but the absolute instant is preserved.\n *\n * @example\n * ```ts\n * // Re-projection: same instant, different wall-clock\n * toZoned(parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]'), { tz: 'UTC' })\n * // 2026-03-21T10:00:00+00:00[UTC] ← wall-clock changed from 11:00 → 10:00\n * ```\n */\nexport function toZoned(\n input: TimeInput,\n options: TimeOptionsWithTz | (WithPrefer & { tz: string }),\n): Temporal.ZonedDateTime {\n const opts = options as TimeOptionsWithTz & WithPrefer;\n const tz = validateTz(opts.tz);\n\n if (input instanceof Temporal.ZonedDateTime) return input.withTimeZone(tz);\n\n if (input instanceof Temporal.PlainDateTime) {\n return input.toZonedDateTime(tz, {\n disambiguation: opts.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n });\n }\n\n if (input instanceof Temporal.PlainDate) {\n return input.toZonedDateTime({ timeZone: tz });\n }\n\n if (input instanceof Temporal.Instant) return input.toZonedDateTimeISO(tz);\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into a specific timezone as a `ZonedDateTime`.\n * Unlike {@link toZoned}, this is the clean public API: explicit `tz` string parameter\n * rather than an options bag, signalling intent clearly.\n *\n * When `input` is already a `ZonedDateTime`, it is re-projected — same instant, new zone.\n *\n * @example\n * ```ts\n * inTz(parseInstant('2026-03-21T10:00:00Z'), 'Europe/Berlin')\n * // 2026-03-21T11:00:00+01:00[Europe/Berlin]\n * ```\n */\nexport function inTz(input: TimeInput, tz: string): Temporal.ZonedDateTime {\n return toZoned(input, { tz });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, DifferenceOptions, ParseAs, ShiftOptions, TimeInput } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { CALENDAR_UNITS, inferSharedTimeZone, inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\ntype TimeOptionsWithTz = { tz: string };\n\n/**\n * Returns the current date and time in the given timezone.\n *\n * @example\n * ```ts\n * now('America/New_York').hour; // current hour in New York\n * ```\n */\nexport function now(tz: string): Temporal.ZonedDateTime {\n return Temporal.Now.zonedDateTimeISO(tz);\n}\n\n/**\n * Returns the current absolute instant (UTC point in time).\n * Use this instead of `Temporal.Now.instant()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * timeDiff(nowInstant()) // { unit: 'millisecond', value: 0 } (compared to now)\n * expires(nowInstant(), { expired: { days: 0 }, safe: { years: 100 } }) // 'safe'\n * ```\n */\nexport function nowInstant(): Temporal.Instant {\n return Temporal.Now.instant();\n}\n\n/**\n * Parses a full ISO 8601 zoned date-time string into a `ZonedDateTime`.\n * Use this instead of `Temporal.ZonedDateTime.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]')\n * parseZoned('2026-03-21T00:00:00[UTC]')\n * ```\n */\nexport function parseZoned(input: string): Temporal.ZonedDateTime {\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n fail(\n `Invalid zoned date-time string: \"${input}\". Expected an ISO 8601 string with offset and timezone (e.g. 2026-03-21T10:00:00+01:00[Europe/Berlin]).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 date-only string into a timezone-free `PlainDate`.\n * Use this instead of `Temporal.PlainDate.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parsePlainDate('2026-03-21') // 2026-03-21\n * ```\n */\nexport function parsePlainDate(input: string): Temporal.PlainDate {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n fail(`Invalid plain date string: \"${input}\". Expected an ISO 8601 date string (e.g. YYYY-MM-DD).`);\n }\n}\n\n/**\n * Parses an ISO 8601 string into a timezone-free `PlainDateTime` (wall-clock time).\n * Use {@link toInstant} or {@link inTz} to attach a timezone when needed.\n *\n * @example\n * ```ts\n * parsePlainDateTime('2026-03-21') // 2026-03-21T00:00:00\n * parsePlainDateTime('2026-03-21T10:15:30') // 2026-03-21T10:15:30\n * ```\n */\nexport function parsePlainDateTime(input: string): Temporal.PlainDateTime {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n fail(\n `Invalid date/time string: \"${input}\". Expected an ISO 8601 date or date-time string (e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 UTC string into an absolute `Instant`.\n *\n * @example\n * ```ts\n * parseInstant('2026-03-21T10:15:30Z')\n * ```\n */\nexport function parseInstant(input: string): Temporal.Instant {\n try {\n return Temporal.Instant.from(input);\n } catch {\n fail(`Invalid instant string: \"${input}\". Expected an ISO 8601 UTC string (e.g. YYYY-MM-DDTHH:mm:ssZ).`);\n }\n}\n\n/**\n * DST-safe date arithmetic. Adds `duration` to `input` and returns the result as a\n * `ZonedDateTime`. Handles spring-forward and fall-back correctly.\n *\n * **Always returns a `ZonedDateTime`** — even when the input is an `Instant`.\n * Call `.toInstant()` on the result if you need an `Instant` back.\n * Requires `options.tz` when input is an `Instant`, `PlainDate`, or `PlainDateTime`.\n *\n * @example\n * ```ts\n * shift(parseZoned('2026-03-08T01:30:00-05:00[America/New_York]'), { hours: 1 })\n * // 2026-03-08T03:30:00-04:00[America/New_York] (skipped the missing hour)\n *\n * // Instant input — tz required, result is ZonedDateTime\n * shift(parseInstant('2026-03-21T10:00:00Z'), { hours: 2 }, { tz: 'UTC' }).toInstant()\n * ```\n */\nexport function shift(\n input: Temporal.ZonedDateTime,\n duration: Temporal.DurationLike,\n options?: ShiftOptions,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: Temporal.Instant | Temporal.PlainDate | Temporal.PlainDateTime,\n duration: Temporal.DurationLike,\n options: ShiftOptions & TimeOptionsWithTz,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: TimeInput,\n duration: Temporal.DurationLike,\n options: ShiftOptions = {},\n): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { prefer: options.prefer, tz }).add(duration);\n}\n\n/**\n * Returns the calendar-aware duration between `start` and `end`.\n *\n * When both inputs are `Instant` and no calendar unit is requested, the fast\n * path skips timezone conversion. Calendar units (`day`, `week`, `month`, `year`)\n * always require a timezone — pass `options.tz` or use `ZonedDateTime` inputs.\n * `options.prefer` (DST disambiguation) is only meaningful for `PlainDateTime` inputs.\n *\n * @example\n * ```ts\n * difference(\n * parseZoned('2026-03-08T00:00:00-05:00[America/New_York]'),\n * parseZoned('2026-03-09T00:00:00-04:00[America/New_York]'),\n * { largestUnit: 'hour' },\n * ).hours // 23 (DST spring-forward day)\n * ```\n */\nexport function difference(start: TimeInput, end: TimeInput, options: DifferenceOptions = {}): Temporal.Duration {\n const { largestUnit, prefer, roundingIncrement, roundingMode, smallestUnit } = options;\n const roundingOptions = { largestUnit, roundingIncrement, roundingMode, smallestUnit };\n\n const needsCalendar =\n (largestUnit !== undefined && CALENDAR_UNITS.has(largestUnit as CalendarUnit)) ||\n (smallestUnit !== undefined && CALENDAR_UNITS.has(smallestUnit as CalendarUnit));\n\n if (!needsCalendar && start instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return end.since(start, roundingOptions as Temporal.DifferenceOptions<Temporal.TimeUnit>);\n }\n\n const tz = inferSharedTimeZone([start, end], options);\n\n return toZoned(end, { prefer, tz }).since(toZoned(start, { prefer, tz }), roundingOptions);\n}\n\n/**\n * Type guard that checks whether `value` is a valid `TimeInput`.\n *\n * @example\n * ```ts\n * isValid(parseInstant('2026-03-21T10:00:00Z')) // true\n * isValid('2026-03-21') // false\n * ```\n */\nexport function isValid(value: unknown): value is TimeInput {\n return (\n value instanceof Temporal.Instant ||\n value instanceof Temporal.ZonedDateTime ||\n value instanceof Temporal.PlainDateTime ||\n value instanceof Temporal.PlainDate\n );\n}\n\n/**\n * Parses any ISO 8601 string into the most specific `TimeInput` type possible.\n * Tries ZonedDateTime → Instant → PlainDateTime → PlainDate in order.\n * Throws a descriptive `TypeError` if none match.\n *\n * Pass `as` to request a specific return type (throws if the string cannot be parsed as that type):\n *\n * @example\n * ```ts\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]') // TimeInput (auto-detect)\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]', 'zoned') // Temporal.ZonedDateTime\n * parse('2026-03-21T10:00:00Z', 'instant') // Temporal.Instant\n * parse('2026-03-21T10:00:00', 'plain-datetime') // Temporal.PlainDateTime\n * parse('2026-03-21', 'plain-date') // Temporal.PlainDate\n * ```\n */\nexport function parse(input: string, as: 'zoned'): Temporal.ZonedDateTime;\nexport function parse(input: string, as: 'instant'): Temporal.Instant;\nexport function parse(input: string, as: 'plain-datetime'): Temporal.PlainDateTime;\nexport function parse(input: string, as: 'plain-date'): Temporal.PlainDate;\nexport function parse(input: string, as?: ParseAs): TimeInput;\nexport function parse(input: string, as?: ParseAs): TimeInput {\n if (as === 'zoned') return parseZoned(input);\n\n if (as === 'instant') return parseInstant(input);\n\n if (as === 'plain-datetime') return parsePlainDateTime(input);\n\n if (as === 'plain-date') return parsePlainDate(input);\n\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n /* try next format */\n }\n\n try {\n return Temporal.Instant.from(input);\n } catch {\n /* try next format */\n }\n\n // Try PlainDateTime before PlainDate — a date-only string (no 'T') will also\n // be accepted by PlainDateTime.from(), producing midnight, so we check the\n // string to pick the most specific type.\n if (input.includes('T')) {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n /* fall through to error */\n }\n } else {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n /* fall through to error */\n }\n }\n\n fail(\n `Unable to parse date/time string: \"${input}\". Expected ISO 8601 ZonedDateTime, Instant, PlainDateTime, or PlainDate.`,\n );\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, DateTimeDisambiguation, TimeInput } from './types';\n\nimport { toZoned } from './_convert';\n\n// ─── Floor-to-boundary-unit helper ───────────────────────────────────────────\n\nconst TIME_ZERO: Temporal.ZonedDateTimeLike = {\n hour: 0,\n microsecond: 0,\n millisecond: 0,\n minute: 0,\n nanosecond: 0,\n second: 0,\n};\n\nconst BOUNDARY_CLEAR: Record<Exclude<BoundaryUnit, 'week'>, Temporal.ZonedDateTimeLike> = {\n day: TIME_ZERO,\n hour: { microsecond: 0, millisecond: 0, minute: 0, nanosecond: 0, second: 0 },\n minute: { microsecond: 0, millisecond: 0, nanosecond: 0, second: 0 },\n month: { ...TIME_ZERO, day: 1 },\n year: { ...TIME_ZERO, day: 1, month: 1 },\n};\n\n/**\n * Floors `input` to the start of `unit` in `tz`, returning an `Instant`.\n * Used internally by both `boundary.ts` and `compare.ts` without either depending on the other.\n */\nexport function floorToUnit(\n input: TimeInput,\n unit: BoundaryUnit,\n options: { prefer?: DateTimeDisambiguation; tz: string; weekStartsOn?: number },\n): Temporal.Instant {\n const zoned = toZoned(input, { prefer: options.prefer, tz: options.tz });\n\n if (unit === 'week') {\n const daysToSubtract = (zoned.dayOfWeek - (options.weekStartsOn ?? 1) + 7) % 7;\n\n return zoned.subtract({ days: daysToSubtract }).with(TIME_ZERO).toInstant();\n }\n\n return zoned.with(BOUNDARY_CLEAR[unit]).toInstant();\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryOptions, BoundaryUnit, TimeInput } from './types';\n\nimport { floorToUnit } from './_floor';\nimport { inferTimeZone } from './_tz';\n\n// ─── Boundary step durations ──────────────────────────────────────────────────\n\nconst BOUNDARY_STEP: Record<BoundaryUnit, Temporal.DurationLike> = {\n day: { days: 1 },\n hour: { hours: 1 },\n minute: { minutes: 1 },\n month: { months: 1 },\n week: { weeks: 1 },\n year: { years: 1 },\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns the start of the given `unit` in the inferred or explicit timezone.\n *\n * @example\n * ```ts\n * startOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T00:00:00+00:00[UTC]\n *\n * startOf(instant, 'week', { tz: 'UTC', weekStartsOn: 1 })\n * // Monday of the current week\n * ```\n */\nexport function startOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn }).toZonedDateTimeISO(tz);\n}\n\n/**\n * Returns the last nanosecond of the given `unit` (exactly 1 ns before the next unit starts).\n *\n * @example\n * ```ts\n * endOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T23:59:59.999999999+00:00[UTC]\n * ```\n */\nexport function endOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n const startInstant = floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn });\n\n return startInstant.toZonedDateTimeISO(tz).add(BOUNDARY_STEP[unit]).subtract({ nanoseconds: 1 });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, CompareOptions, TimeInput } from './types';\n\nimport { toInstant } from './_convert';\nimport { floorToUnit } from './_floor';\nimport { inferSharedTimeZone, normalizeRange } from './_tz';\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveFlooredPair(\n a: TimeInput,\n b: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { left: Temporal.Instant; right: Temporal.Instant } {\n const tz = inferSharedTimeZone([a, b], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n\n return {\n left: floorToUnit(a, unit, unitOpts),\n right: floorToUnit(b, unit, unitOpts),\n };\n}\n\nfunction resolveFlooredTriple(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { lower: Temporal.Instant; target: Temporal.Instant; upper: Temporal.Instant } {\n const tz = inferSharedTimeZone([value, start, end], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n const target = floorToUnit(value, unit, unitOpts);\n const [lower, upper] = normalizeRange(floorToUnit(start, unit, unitOpts), floorToUnit(end, unit, unitOpts));\n\n return { lower, target, upper };\n}\n\nfunction compareByUnit(a: TimeInput, b: TimeInput, options: CompareOptions): number {\n if (!options.unit) {\n return Temporal.Instant.compare(toInstant(a, options), toInstant(b, options));\n }\n\n const { left, right } = resolveFlooredPair(a, b, options.unit, options);\n\n return Temporal.Instant.compare(left, right);\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when `a` is strictly before `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isBefore(parseInstant('2026-03-21T10:00:00Z'), parseInstant('2026-03-21T11:00:00Z'))\n * // true\n * ```\n */\nexport function isBefore(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) < 0;\n}\n\n/**\n * Returns `true` when `a` is strictly after `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isAfter(parseInstant('2026-03-21T11:00:00Z'), parseInstant('2026-03-21T10:00:00Z'))\n * // true\n * ```\n */\nexport function isAfter(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) > 0;\n}\n\n/**\n * Returns `true` when `a` and `b` represent the same point (or boundary unit) in time.\n *\n * @example\n * ```ts\n * isSame(a, b, { tz: 'America/New_York', unit: 'day' })\n * // true when a and b fall on the same calendar day in New York\n * ```\n */\nexport function isSame(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) === 0;\n}\n\n/**\n * Returns `true` when `value` falls within `[start, end]` (inclusive, bounds normalized).\n * Pass `options.unit` to floor all three inputs to a calendar boundary before comparing.\n *\n * @example\n * ```ts\n * within(\n * parseInstant('2026-03-21T11:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ) // true\n * ```\n */\nexport function within(value: TimeInput, start: TimeInput, end: TimeInput, options: CompareOptions = {}): boolean {\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n}\n\n/**\n * Clamps `value` to within `[start, end]` (bounds normalized).\n *\n * When `value` is a `ZonedDateTime`, returns a `ZonedDateTime` in the same timezone.\n * Otherwise returns an `Instant`.\n *\n * When `options.unit` is set, all three inputs are floored to that calendar boundary before\n * clamping — the result is at the start of the boundary unit, not the original time-of-day.\n *\n * @example\n * ```ts\n * clamp(\n * parseInstant('2026-03-21T13:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ).toString() // '2026-03-21T12:00:00Z'\n *\n * clamp(\n * parseZoned('2026-03-21T13:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T10:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T12:00:00+00:00[UTC]'),\n * ).toString() // '2026-03-21T12:00:00+00:00[UTC]'\n * ```\n */\nexport function clamp(\n value: Temporal.ZonedDateTime,\n start: TimeInput,\n end: TimeInput,\n options?: CompareOptions,\n): Temporal.ZonedDateTime;\nexport function clamp(value: TimeInput, start: TimeInput, end: TimeInput, options?: CompareOptions): Temporal.Instant;\nexport function clamp(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n options: CompareOptions = {},\n): Temporal.Instant | Temporal.ZonedDateTime {\n const isZoned = value instanceof Temporal.ZonedDateTime;\n const tz = isZoned ? value.timeZoneId : undefined;\n\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n return isZoned && tz ? clamped.toZonedDateTimeISO(tz) : clamped;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n // For unit-based clamping, resolve the output tz from options or from value's zone\n const outTz = options.tz ?? tz ?? inferSharedTimeZone([value, start, end], options);\n\n return isZoned ? clamped.toZonedDateTimeISO(outTz) : clamped;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type {\n DurationFormatOptions,\n FormatOptions,\n FormatPattern,\n RelativeFormatOptions,\n RelativeTimeInput,\n TimeDiffResult,\n TimeInput,\n TimeOptions,\n} from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\n// ─── Formatter types ──────────────────────────────────────────────────────────\n\ntype DurationFormatter = { format(value: Temporal.Duration): string };\ntype DurationFormatterConstructor = new (\n locales?: Intl.LocalesArgument,\n options?: { style?: 'digital' | 'long' | 'narrow' | 'short' },\n) => DurationFormatter;\n\n// ─── Formatter caches ─────────────────────────────────────────────────────────\n\nconst FORMATTER_CACHE_MAX = 128;\n\nfunction cappedGetOrCreate<V>(cache: Map<string, V>, key: string, factory: () => V): V {\n const cached = cache.get(key);\n\n if (cached !== undefined) return cached;\n\n if (cache.size >= FORMATTER_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n\n if (oldest !== undefined) cache.delete(oldest);\n }\n\n const value = factory();\n\n cache.set(key, value);\n\n return value;\n}\n\nconst DATE_TIME_FORMATTER_CACHE = new Map<string, Intl.DateTimeFormat>();\nconst RELATIVE_TIME_FORMATTER_CACHE = new Map<string, Intl.RelativeTimeFormat>();\nconst DURATION_FORMATTER_CACHE = new Map<string, DurationFormatter>();\n\n// ─── Format presets ───────────────────────────────────────────────────────────\n\nconst FORMAT_PRESETS: Record<FormatPattern, Intl.DateTimeFormatOptions> = {\n 'date-only': { dateStyle: 'short' },\n long: { dateStyle: 'full', timeStyle: 'long' },\n medium: { dateStyle: 'medium', timeStyle: 'short' },\n short: { dateStyle: 'short', timeStyle: 'short' },\n 'time-only': { timeStyle: 'short' },\n};\n\n// ─── Formatter factory helpers ────────────────────────────────────────────────\n\nfunction serializeIntlOptions(options: Intl.DateTimeFormatOptions): string {\n return JSON.stringify(\n Object.entries(options)\n .filter(([, value]) => value !== undefined)\n .sort(([l], [r]) => l.localeCompare(r))\n .map(([key, value]) => [key, String(value)]),\n );\n}\n\nfunction makeFormatter(options: FormatOptions, fallbackTz?: string): Intl.DateTimeFormat {\n const tz = options.tz ?? fallbackTz;\n const locale = options.locale;\n\n if (options.intl !== undefined) {\n const cacheKey = `${String(locale ?? '')}|intl|${tz ?? ''}|${serializeIntlOptions(options.intl)}`;\n\n return cappedGetOrCreate(DATE_TIME_FORMATTER_CACHE, cacheKey, () => {\n const intlOptions = tz !== undefined ? { ...options.intl, timeZone: tz } : options.intl;\n\n return new Intl.DateTimeFormat(locale, intlOptions);\n });\n }\n\n const pattern = options.pattern ?? 'medium';\n const cacheKey = `${String(locale ?? '')}|${pattern}|${tz ?? ''}`;\n\n return cappedGetOrCreate(\n DATE_TIME_FORMATTER_CACHE,\n cacheKey,\n () => new Intl.DateTimeFormat(locale, { ...FORMAT_PRESETS[pattern], timeZone: tz }),\n );\n}\n\nfunction getRelativeFormatter(options: {\n locale?: Intl.LocalesArgument;\n numeric?: Intl.RelativeTimeFormatNumeric;\n style?: Intl.RelativeTimeFormatStyle;\n}): Intl.RelativeTimeFormat {\n const cacheKey = `${String(options.locale ?? '')}|${options.numeric ?? 'auto'}|${options.style ?? 'long'}`;\n\n return cappedGetOrCreate(\n RELATIVE_TIME_FORMATTER_CACHE,\n cacheKey,\n () =>\n new Intl.RelativeTimeFormat(options.locale, {\n numeric: options.numeric ?? 'auto',\n style: options.style ?? 'long',\n }),\n );\n}\n\nfunction getDurationFormatter(options: {\n locale?: Intl.LocalesArgument;\n style?: 'digital' | 'long' | 'narrow' | 'short';\n}): DurationFormatter | null {\n const IntlWithDurationFormat = Intl as typeof Intl & { DurationFormat?: DurationFormatterConstructor };\n\n if (!IntlWithDurationFormat.DurationFormat) return null;\n\n const cacheKey = `${String(options.locale ?? '')}|${options.style ?? ''}`;\n\n return cappedGetOrCreate(\n DURATION_FORMATTER_CACHE,\n cacheKey,\n () => new IntlWithDurationFormat.DurationFormat!(options.locale, { style: options.style }),\n );\n}\n\n// ─── Time scale constants ─────────────────────────────────────────────────────\n\nconst SECONDS_PER_MINUTE = 60;\nconst SECONDS_PER_HOUR = 3_600;\nconst SECONDS_PER_DAY = 86_400;\nconst SECONDS_PER_WEEK = 604_800;\nconst SECONDS_PER_MONTH = 2_629_800; // ≈ 30.4375 days × 86400\nconst SECONDS_PER_YEAR = 31_557_600; // 365.25 days × 86400\n\n// ─── Relative time helpers ────────────────────────────────────────────────────\n\nconst RELATIVE_UNITS: ReadonlyArray<{ scale: number; thresholdToPromote: number; unit: Intl.RelativeTimeFormatUnit }> =\n [\n { scale: 1, thresholdToPromote: SECONDS_PER_MINUTE, unit: 'second' },\n { scale: SECONDS_PER_MINUTE, thresholdToPromote: SECONDS_PER_HOUR / SECONDS_PER_MINUTE, unit: 'minute' },\n { scale: SECONDS_PER_HOUR, thresholdToPromote: SECONDS_PER_DAY / SECONDS_PER_HOUR, unit: 'hour' },\n { scale: SECONDS_PER_DAY, thresholdToPromote: SECONDS_PER_WEEK / SECONDS_PER_DAY, unit: 'day' },\n { scale: SECONDS_PER_WEEK, thresholdToPromote: SECONDS_PER_MONTH / SECONDS_PER_WEEK, unit: 'week' },\n { scale: SECONDS_PER_MONTH, thresholdToPromote: 12, unit: 'month' },\n { scale: SECONDS_PER_YEAR, thresholdToPromote: Number.POSITIVE_INFINITY, unit: 'year' },\n ];\n\nfunction toRelativeUnit(seconds: number): { unit: Intl.RelativeTimeFormatUnit; value: number } {\n if (!Number.isFinite(seconds)) fail('formatRelative received a non-finite time difference.');\n\n const roundedSeconds = Math.round(seconds);\n\n for (const { scale, thresholdToPromote, unit } of RELATIVE_UNITS) {\n const value = Math.round(roundedSeconds / scale);\n\n if (Math.abs(value) < thresholdToPromote) return { unit, value };\n }\n\n return { unit: 'year', value: Math.round(roundedSeconds / SECONDS_PER_YEAR) };\n}\n\n// ─── Duration fallback renderer ───────────────────────────────────────────────\n\n// All English duration unit names follow the same pluralization rule: singular = plural.slice(0, -1)\nconst DURATION_UNITS = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n] as const satisfies ReadonlyArray<keyof Temporal.Duration>;\n\n// English-only fallback; runs only when Intl.DurationFormat is unavailable in the runtime.\nfunction buildDurationFallback(duration: Temporal.Duration): string {\n const parts: string[] = [];\n\n for (const unit of DURATION_UNITS) {\n const value = Math.abs(duration[unit] as number);\n\n if (value !== 0) parts.push(`${value} ${value === 1 ? unit.slice(0, -1) : unit}`);\n }\n\n return parts.length === 0 ? '0 seconds' : parts.join(', ');\n}\n\n// ─── Private helpers ──────────────────────────────────────────────────────────\n\n/**\n * Resolves a shared display timezone for two-input range functions.\n * Throws when both inputs are `ZonedDateTime` with different zones and no `options.tz` override.\n */\nfunction resolveRangeTz(start: TimeInput, end: TimeInput, options: FormatOptions, caller: string): string | undefined {\n if (options.tz) return options.tz;\n\n const startTz = start instanceof Temporal.ZonedDateTime ? start.timeZoneId : undefined;\n const endTz = end instanceof Temporal.ZonedDateTime ? end.timeZoneId : undefined;\n\n if (startTz && endTz && startTz !== endTz) {\n fail(`${caller} received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`);\n }\n\n return startTz ?? endTz;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Formats `input` using `Intl.DateTimeFormat`. Defaults to `pattern: 'medium'`.\n *\n * Pass `intl` for full `Intl.DateTimeFormatOptions` control (mutually exclusive with `pattern`).\n * The timezone is inferred from a `ZonedDateTime` input or from `options.tz`.\n *\n * @example\n * ```ts\n * format(parseInstant('2026-03-21T10:15:30Z'), { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:15'\n * ```\n */\nexport function format(input: TimeInput, options: FormatOptions = {}): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).format(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Formats a time span between `start` and `end` using `Intl.DateTimeFormat.formatRange`.\n *\n * @example\n * ```ts\n * formatRange(start, end, { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:00 – 12:00'\n * ```\n */\nexport function formatRange(start: TimeInput, end: TimeInput, options: FormatOptions = {}): string {\n const tz = resolveRangeTz(start, end, options, 'formatRange');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRange(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Returns the raw `Intl.DateTimeRangeFormatPart[]` array for a time span, enabling\n * fine-grained rendering of range start, end, and shared parts separately.\n *\n * @example\n * ```ts\n * formatRangeParts(start, end, { locale: 'en-US', pattern: 'short', tz: 'UTC' })\n * // [{ type: 'month', value: '3', source: 'startRange' }, ...]\n * ```\n */\nexport function formatRangeParts(\n start: TimeInput,\n end: TimeInput,\n options: FormatOptions = {},\n): ReturnType<Intl.DateTimeFormat['formatRangeToParts']> {\n const tz = resolveRangeTz(start, end, options, 'formatRangeParts');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRangeToParts(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Serializes `input` to a UTC ISO 8601 instant string (`2026-03-21T10:15:30Z`).\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * @example\n * ```ts\n * formatInstant(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T10:15:30Z'\n * ```\n */\nexport function formatInstant(input: TimeInput, options: TimeOptions = {}): string {\n return toInstant(input, options).toString();\n}\n\n/**\n * Serializes `input` to a zoned ISO 8601 string (`2026-03-21T11:15:30+01:00[Europe/Berlin]`).\n *\n * @param options.tz - Required when `input` is a `PlainDate` or `PlainDateTime`.\n * Inferred automatically from a `ZonedDateTime` or `Instant` input.\n *\n * @throws {TempoError} When `input` is a `PlainDate` or `PlainDateTime` and `options.tz` is omitted.\n *\n * @example\n * ```ts\n * formatZoned(parseInstant('2026-03-21T10:15:30Z'), { tz: 'Europe/Berlin' })\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]'\n *\n * formatZoned(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]' (tz inferred)\n * ```\n */\nexport function formatZoned(input: TimeInput, options: TimeOptions = {}): string {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { tz }).toString();\n}\n\n/**\n * Formats `input` relative to `options.base` (defaults to now) using `Intl.RelativeTimeFormat`.\n *\n * @example\n * ```ts\n * formatRelative(parseInstant('2026-03-21T12:00:00Z'), {\n * base: parseInstant('2026-03-21T10:00:00Z'),\n * locale: 'en-US',\n * numeric: 'always',\n * })\n * // 'in 2 hours'\n * ```\n */\nexport function formatRelative(input: RelativeTimeInput, options: RelativeFormatOptions = {}): string {\n const target = input instanceof Temporal.Instant ? input : input.toInstant();\n const base = options.base\n ? options.base instanceof Temporal.Instant\n ? options.base\n : options.base.toInstant()\n : Temporal.Now.instant();\n const differenceInSeconds = (target.epochMilliseconds - base.epochMilliseconds) / 1000;\n const { unit, value } = toRelativeUnit(differenceInSeconds);\n\n return getRelativeFormatter(options).format(value, unit);\n}\n\n/**\n * Parses an ISO duration string or `Temporal.DurationLike` into a `Temporal.Duration`.\n *\n * @example\n * ```ts\n * parseDuration('PT2H30M').toString() // 'PT2H30M'\n * parseDuration({ hours: 2, minutes: 30 }).toString() // 'PT2H30M'\n * ```\n */\nexport function parseDuration(input: string | Temporal.DurationLike): Temporal.Duration {\n try {\n return Temporal.Duration.from(input);\n } catch {\n fail(`Invalid duration input: \"${String(input)}\". Expected an ISO 8601 duration string or Temporal.DurationLike.`);\n }\n}\n\n/**\n * Formats a duration using `Intl.DurationFormat` when available, falling back to\n * a human-readable plain-English string.\n *\n * @example\n * ```ts\n * formatDuration('PT2H30M', { locale: 'en-US', style: 'long' })\n * // '2 hours, 30 minutes'\n * ```\n */\nexport function formatDuration(input: string | Temporal.DurationLike, options: DurationFormatOptions = {}): string {\n const duration = parseDuration(input);\n const formatter = getDurationFormatter(options);\n\n if (formatter) return formatter.format(duration);\n\n return buildDurationFallback(duration);\n}\n\n/**\n * Returns the raw `Intl.DateTimeFormatPart[]` array for `input`, enabling\n * custom rendering where individual parts (year, month, day, etc.) need\n * to be styled or composed differently.\n *\n * @example\n * ```ts\n * formatParts(parseInstant('2026-03-21T10:15:30Z'), { pattern: 'medium', tz: 'UTC' })\n * // [{ type: 'month', value: 'Mar' }, { type: 'literal', value: ' ' }, ...]\n * ```\n */\nexport function formatParts(input: TimeInput, options: FormatOptions = {}): Intl.DateTimeFormatPart[] {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).formatToParts(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Converts a `TimeDiffResult` to a human-readable string.\n * Uses the singular unit name when value is 1, plural (unit + 's') otherwise.\n *\n * Pass `options.locale` to localize the numeric part via `Intl.NumberFormat`.\n * Unit names remain English — for fully localized output use {@link formatRelative}\n * or {@link formatDuration} instead.\n *\n * @example\n * ```ts\n * humanize({ unit: 'day', value: 1 }) // '1 day'\n * humanize({ unit: 'day', value: 3 }) // '3 days'\n * humanize({ unit: 'day', value: 3 }, { locale: 'ar' }) // '٣ days'\n * humanize({ unit: 'millisecond', value: 0 }) // '0 milliseconds'\n * ```\n */\nexport function humanize(diff: TimeDiffResult, options: { locale?: Intl.LocalesArgument } = {}): string {\n const { unit, value } = diff;\n const formatted = options.locale ? new Intl.NumberFormat(options.locale).format(value) : String(value);\n\n return `${formatted} ${value === 1 ? unit : `${unit}s`}`;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { TimeDiffResult, TimeDiffUnit, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferSharedTimeZone, MS_PER_MONTH } from './_tz';\n\n// ─── Threshold sort cache ─────────────────────────────────────────────────────\n\ntype SortedThreshold<K extends string> = { key: K; ms: number }[];\n\nconst THRESHOLD_SORT_CACHE = new WeakMap<object, SortedThreshold<string>>();\n\nfunction getSortedThresholds<K extends string>(thresholds: Record<K, Temporal.DurationLike>): SortedThreshold<K> {\n const cached = THRESHOLD_SORT_CACHE.get(thresholds);\n\n if (cached) return cached as SortedThreshold<K>;\n\n const sorted = (Object.keys(thresholds) as K[])\n .map((key) => ({ key, ms: durationToMs(thresholds[key]) }))\n .sort((a, b) => a.ms - b.ms);\n\n THRESHOLD_SORT_CACHE.set(thresholds, sorted);\n\n return sorted;\n}\n\n// ─── expires ─────────────────────────────────────────────────────────────────\n\n/**\n * Classifies a date into a user-defined bucket by comparing diff = date − now\n * against the provided thresholds (sorted ascending). Returns the key of the\n * first threshold the diff falls within, or `null` if no threshold matches.\n *\n * Thresholds accept negative durations to classify past dates. The function\n * requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * **Performance:** threshold objects are cached by reference in a `WeakMap`. Define\n * the threshold record at module scope (not inline) so sorting is performed only once\n * per unique object.\n *\n * @example\n * ```ts\n * expires(expiresAt, {\n * longExpired: { days: -30 }, // more than 30 days in the past\n * expired: { days: 0 }, // any past date\n * critical: { days: 3 }, // within 3 days\n * warning: { days: 14 }, // within 14 days\n * safe: { years: 100 }, // catch-all for far future\n * })\n * // → 'longExpired' | 'expired' | 'critical' | 'warning' | 'safe' | null\n * ```\n */\nexport function expires<K extends string>(\n date: TimeInput,\n thresholds: Record<K, Temporal.DurationLike>,\n options: TimeOptions = {},\n now = Temporal.Now.instant(),\n): K | null {\n const dateMs = toInstant(date, options).epochMilliseconds;\n const nowMs = now.epochMilliseconds;\n\n // diff is positive for future dates, negative for past dates (date − now)\n const diffMs = dateMs - nowMs;\n\n for (const { key, ms } of getSortedThresholds(thresholds)) {\n if (diffMs <= ms) return key;\n }\n\n return null;\n}\n\n/** Converts a `DurationLike` to approximate milliseconds for threshold comparison. */\nfunction durationToMs(duration: Temporal.DurationLike): number {\n const d = Temporal.Duration.from(duration);\n\n // Use approximate conversions — thresholds are human-defined boundaries, not calendar-precise.\n return (\n (d.years ?? 0) * 12 * MS_PER_MONTH +\n (d.months ?? 0) * MS_PER_MONTH +\n (d.weeks ?? 0) * 7 * 86_400_000 +\n (d.days ?? 0) * 86_400_000 +\n (d.hours ?? 0) * 3_600_000 +\n (d.minutes ?? 0) * 60_000 +\n (d.seconds ?? 0) * 1_000 +\n (d.milliseconds ?? 0) +\n (d.microseconds ?? 0) / 1_000 +\n (d.nanoseconds ?? 0) / 1_000_000\n );\n}\n\n// ─── timeDiff ─────────────────────────────────────────────────────────────────\n\nconst UNIT_ORDER: ReadonlyArray<{ field: keyof Temporal.Duration; unit: TimeDiffUnit }> = [\n { field: 'years', unit: 'year' },\n { field: 'months', unit: 'month' },\n { field: 'weeks', unit: 'week' },\n { field: 'days', unit: 'day' },\n { field: 'hours', unit: 'hour' },\n { field: 'minutes', unit: 'minute' },\n { field: 'seconds', unit: 'second' },\n { field: 'milliseconds', unit: 'millisecond' },\n];\n\nfunction sinceZoned(a: Temporal.ZonedDateTime, b: Temporal.ZonedDateTime): Temporal.Duration {\n return Temporal.ZonedDateTime.compare(a, b) <= 0\n ? b.since(a, { largestUnit: 'year' })\n : a.since(b, { largestUnit: 'year' });\n}\n\nfunction pickLargestUnit(duration: Temporal.Duration): TimeDiffResult {\n for (const { field, unit } of UNIT_ORDER) {\n const value = Math.abs(duration[field] as number);\n\n if (value > 0) return { unit, value };\n }\n\n return { unit: 'millisecond', value: 0 };\n}\n\n/**\n * Returns the absolute calendar-accurate difference between two dates as a\n * structured `{ unit, value }` in the largest meaningful unit.\n *\n * When `b` is omitted, the current instant is used.\n * Requires `options.tz` when inputs are `PlainDate`, `PlainDateTime`, or plain `Instant` with\n * calendar-unit precision. Throws when timezone cannot be inferred from inputs.\n *\n * @example\n * ```ts\n * timeDiff(\n * parseInstant('2026-01-01T00:00:00Z'),\n * parseInstant('2027-03-15T00:00:00Z'),\n * )\n * // { unit: 'year', value: 1 }\n * ```\n */\nexport function timeDiff(a: TimeInput, b?: TimeInput, options: TimeOptions = {}): TimeDiffResult {\n const end: TimeInput = b ?? Temporal.Now.instant();\n\n // Fast path: two Instants with no explicit tz — project to UTC for calendar-accurate units.\n // Instants are absolute and timezone-independent; UTC is the canonical calendar context.\n if (!options.tz && a instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return pickLargestUnit(sinceZoned(a.toZonedDateTimeISO('UTC'), end.toZonedDateTimeISO('UTC')));\n }\n\n // Plain inputs or calendar-accurate comparison require a timezone.\n const tz = inferSharedTimeZone([a, end], options);\n\n return pickLargestUnit(sinceZoned(toZoned(a, { tz }), toZoned(end, { tz })));\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { RecurrenceRule, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { TempoInvalidInputError } from './errors';\n\n/**\n * Lazily generates `ZonedDateTime` values between `start` and `end` (inclusive),\n * advancing by `step` on each iteration.\n *\n * Returns a generator — use `for...of` for lazy consumption or spread to collect\n * into an array: `[...dateRange(...)]`.\n *\n * @throws {RangeError} when `step` does not advance the date forward. Thrown eagerly at call time.\n *\n * Yields nothing when `start > end` (the generator terminates immediately).\n *\n * When `start` is a `ZonedDateTime`, the timezone is inferred from it. If `end` is in a\n * different timezone, it is silently re-projected into `start`'s timezone. Pass `options.tz`\n * explicitly to override.\n *\n * @example\n * ```ts\n * // Lazy — safe for large ranges\n * for (const day of dateRange(start, end, { days: 1 }, { tz: 'UTC' })) {\n * if (someCondition(day)) break;\n * }\n *\n * // Collect to array\n * const days = [...dateRange(start, end, { days: 1 }, { tz: 'UTC' })];\n *\n * // ZonedDateTime inputs — tz is inferred, no need to pass options\n * const days = [...dateRange(zdtStart, zdtEnd, { days: 1 })];\n * ```\n */\nexport function dateRange(\n start: TimeInput,\n end: TimeInput,\n step: Temporal.DurationLike,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const tz = inferTimeZone(start, options);\n const startZoned = toZoned(start, { ...options, tz });\n const endZoned = toZoned(end, { ...options, tz });\n\n // Eager validation — fires at call time, not on first iteration.\n if (Temporal.ZonedDateTime.compare(startZoned.add(step), startZoned) <= 0) {\n throw new TempoInvalidInputError('dateRange: step must advance the date forward');\n }\n\n return dateRangeGenerator(startZoned, endZoned, step);\n}\n\nfunction* dateRangeGenerator(\n start: Temporal.ZonedDateTime,\n end: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n\n while (Temporal.ZonedDateTime.compare(current, end) <= 0) {\n yield current;\n current = current.add(step);\n }\n}\n\n/**\n * Lazily generates `ZonedDateTime` occurrences according to a recurrence rule.\n *\n * Supports `daily`, `weekly`, `monthly`, and `yearly` frequencies with an optional\n * `interval` (defaults to `1`), `count` limit, and `until` boundary (inclusive).\n * The `RecurrenceRule` type enforces that at least one of `count` or `until` must be\n * provided — this is a compile-time guarantee for TypeScript callers.\n * Passing `count: 0` yields an empty sequence without error.\n *\n * @example\n * ```ts\n * // Every Monday for 4 weeks\n * const mondays = [...recurrence(start, { frequency: 'weekly', count: 4 }, { tz: 'UTC' })];\n *\n * // Bi-weekly until a deadline\n * for (const date of recurrence(start, { frequency: 'weekly', interval: 2, until: deadline }, { tz: 'UTC' })) {\n * schedule(date);\n * }\n *\n * // ZonedDateTime start — tz is inferred, no need to pass options\n * for (const date of recurrence(zdtStart, { frequency: 'daily', count: 7 })) {\n * schedule(date);\n * }\n * ```\n */\nexport function recurrence(\n start: TimeInput,\n rule: RecurrenceRule,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const { count, frequency, interval = 1, until } = rule;\n\n const tz = inferTimeZone(start, options);\n\n const step: Temporal.DurationLike =\n frequency === 'daily'\n ? { days: interval }\n : frequency === 'weekly'\n ? { weeks: interval }\n : frequency === 'monthly'\n ? { months: interval }\n : { years: interval };\n\n const endInstant = until !== undefined ? toInstant(until, { ...options, tz }) : undefined;\n\n return recurrenceGenerator(toZoned(start, { ...options, tz }), step, count, endInstant);\n}\n\nfunction* recurrenceGenerator(\n start: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n count: number | undefined,\n endInstant: Temporal.Instant | undefined,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n let emitted = 0;\n\n while (true) {\n if (count !== undefined && emitted >= count) break;\n\n if (endInstant !== undefined && Temporal.Instant.compare(current.toInstant(), endInstant) > 0) break;\n\n yield current;\n emitted++;\n current = current.add(step);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var Tempo=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},r=class extends n{},i=class extends n{},a=class extends n{},o=class extends n{};function s(e,t=r){throw new t(e)}function c(e){try{t.Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(e)}catch{s(`Unknown or invalid timezone: "${e}". Expected an IANA timezone name (e.g. "America/New_York") or UTC offset (e.g. "+05:30").`,i)}return e}function l(e,n){let r=n.tz??(e instanceof t.Temporal.ZonedDateTime?e.timeZoneId:void 0);return r||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),c(r)}function u(e,n){if(n.tz)return c(n.tz);let r;for(let n of e){if(!(n instanceof t.Temporal.ZonedDateTime))continue;let e=n.timeZoneId;if(!r){r=e;continue}r!==e&&s(`Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`)}return r||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),r}function d(e,n){return t.Temporal.Instant.compare(e,n)<=0?[e,n]:[n,e]}var f=new Set([`day`,`month`,`week`,`year`]),p=30.4375*864e5;function m(e,n={}){if(e instanceof t.Temporal.Instant)return e;if(e instanceof t.Temporal.ZonedDateTime)return e.toInstant();if(e instanceof t.Temporal.PlainDateTime)return n.tz||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),e.toZonedDateTime(c(n.tz),{disambiguation:n.prefer}).toInstant();if(e instanceof t.Temporal.PlainDate)return n.tz||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),e.toZonedDateTime({timeZone:c(n.tz)}).toInstant();s(`Unsupported time input type: ${String(e)}`,o)}function h(e,n){let r=n,i=c(r.tz);if(e instanceof t.Temporal.ZonedDateTime)return e.withTimeZone(i);if(e instanceof t.Temporal.PlainDateTime)return e.toZonedDateTime(i,{disambiguation:r.prefer});if(e instanceof t.Temporal.PlainDate)return e.toZonedDateTime({timeZone:i});if(e instanceof t.Temporal.Instant)return e.toZonedDateTimeISO(i);s(`Unsupported time input type: ${String(e)}`,o)}function ee(e,t){return h(e,{tz:t})}function g(e){return t.Temporal.Now.zonedDateTimeISO(e)}function _(){return t.Temporal.Now.instant()}function v(e){try{return t.Temporal.ZonedDateTime.from(e)}catch{s(`Invalid zoned date-time string: "${e}". Expected an ISO 8601 string with offset and timezone (e.g. 2026-03-21T10:00:00+01:00[Europe/Berlin]).`)}}function y(e){try{return t.Temporal.PlainDate.from(e)}catch{s(`Invalid plain date string: "${e}". Expected an ISO 8601 date string (e.g. YYYY-MM-DD).`)}}function b(e){try{return t.Temporal.PlainDateTime.from(e)}catch{s(`Invalid date/time string: "${e}". Expected an ISO 8601 date or date-time string (e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).`)}}function x(e){try{return t.Temporal.Instant.from(e)}catch{s(`Invalid instant string: "${e}". Expected an ISO 8601 UTC string (e.g. YYYY-MM-DDTHH:mm:ssZ).`)}}function te(e,t,n={}){let r=l(e,n);return h(e,{prefer:n.prefer,tz:r}).add(t)}function S(e,n,r={}){let{largestUnit:i,prefer:a,roundingIncrement:o,roundingMode:s,smallestUnit:c}=r,l={largestUnit:i,roundingIncrement:o,roundingMode:s,smallestUnit:c};if(!(i!==void 0&&f.has(i)||c!==void 0&&f.has(c))&&e instanceof t.Temporal.Instant&&n instanceof t.Temporal.Instant)return n.since(e,l);let d=u([e,n],r);return h(n,{prefer:a,tz:d}).since(h(e,{prefer:a,tz:d}),l)}function C(e){return e instanceof t.Temporal.Instant||e instanceof t.Temporal.ZonedDateTime||e instanceof t.Temporal.PlainDateTime||e instanceof t.Temporal.PlainDate}function w(e,n){if(n===`zoned`)return v(e);if(n===`instant`)return x(e);if(n===`plain-datetime`)return b(e);if(n===`plain-date`)return y(e);try{return t.Temporal.ZonedDateTime.from(e)}catch{}try{return t.Temporal.Instant.from(e)}catch{}if(e.includes(`T`))try{return t.Temporal.PlainDateTime.from(e)}catch{}else try{return t.Temporal.PlainDate.from(e)}catch{}s(`Unable to parse date/time string: "${e}". Expected ISO 8601 ZonedDateTime, Instant, PlainDateTime, or PlainDate.`)}var T={hour:0,microsecond:0,millisecond:0,minute:0,nanosecond:0,second:0},E={day:T,hour:{microsecond:0,millisecond:0,minute:0,nanosecond:0,second:0},minute:{microsecond:0,millisecond:0,nanosecond:0,second:0},month:{...T,day:1},year:{...T,day:1,month:1}};function D(e,t,n){let r=h(e,{prefer:n.prefer,tz:n.tz});if(t===`week`){let e=(r.dayOfWeek-(n.weekStartsOn??1)+7)%7;return r.subtract({days:e}).with(T).toInstant()}return r.with(E[t]).toInstant()}var O={day:{days:1},hour:{hours:1},minute:{minutes:1},month:{months:1},week:{weeks:1},year:{years:1}};function ne(e,t,n={}){let r=l(e,n);return D(e,t,{tz:r,weekStartsOn:n.weekStartsOn}).toZonedDateTimeISO(r)}function re(e,t,n={}){let r=l(e,n);return D(e,t,{tz:r,weekStartsOn:n.weekStartsOn}).toZonedDateTimeISO(r).add(O[t]).subtract({nanoseconds:1})}function ie(e,t,n,r){let i={tz:u([e,t],r),weekStartsOn:r.weekStartsOn};return{left:D(e,n,i),right:D(t,n,i)}}function k(e,t,n,r,i){let a={tz:u([e,t,n],i),weekStartsOn:i.weekStartsOn},o=D(e,r,a),[s,c]=d(D(t,r,a),D(n,r,a));return{lower:s,target:o,upper:c}}function A(e,n,r){if(!r.unit)return t.Temporal.Instant.compare(m(e,r),m(n,r));let{left:i,right:a}=ie(e,n,r.unit,r);return t.Temporal.Instant.compare(i,a)}function ae(e,t,n={}){return A(e,t,n)<0}function oe(e,t,n={}){return A(e,t,n)>0}function se(e,t,n={}){return A(e,t,n)===0}function ce(e,n,r,i={}){if(!i.unit){let a=m(e,i),[o,s]=d(m(n,i),m(r,i));return t.Temporal.Instant.compare(o,a)<=0&&t.Temporal.Instant.compare(a,s)<=0}let{lower:a,target:o,upper:s}=k(e,n,r,i.unit,i);return t.Temporal.Instant.compare(a,o)<=0&&t.Temporal.Instant.compare(o,s)<=0}function j(e,n,r,i={}){let a=e instanceof t.Temporal.ZonedDateTime,o=a?e.timeZoneId:void 0;if(!i.unit){let s=m(e,i),[c,l]=d(m(n,i),m(r,i)),u;return u=t.Temporal.Instant.compare(s,c)<0?c:t.Temporal.Instant.compare(s,l)>0?l:s,a&&o?u.toZonedDateTimeISO(o):u}let{lower:s,target:c,upper:l}=k(e,n,r,i.unit,i),f;f=t.Temporal.Instant.compare(c,s)<0?s:t.Temporal.Instant.compare(c,l)>0?l:c;let p=i.tz??o??u([e,n,r],i);return a?f.toZonedDateTimeISO(p):f}var M=128;function N(e,t,n){let r=e.get(t);if(r!==void 0)return r;if(e.size>=M){let t=e.keys().next().value;t!==void 0&&e.delete(t)}let i=n();return e.set(t,i),i}var P=new Map,F=new Map,I=new Map,L={"date-only":{dateStyle:`short`},long:{dateStyle:`full`,timeStyle:`long`},medium:{dateStyle:`medium`,timeStyle:`short`},short:{dateStyle:`short`,timeStyle:`short`},"time-only":{timeStyle:`short`}};function R(e){return JSON.stringify(Object.entries(e).filter(([,e])=>e!==void 0).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,String(t)]))}function z(e,t){let n=e.tz??t,r=e.locale;if(e.intl!==void 0)return N(P,`${String(r??``)}|intl|${n??``}|${R(e.intl)}`,()=>{let t=n===void 0?e.intl:{...e.intl,timeZone:n};return new Intl.DateTimeFormat(r,t)});let i=e.pattern??`medium`;return N(P,`${String(r??``)}|${i}|${n??``}`,()=>new Intl.DateTimeFormat(r,{...L[i],timeZone:n}))}function B(e){return N(F,`${String(e.locale??``)}|${e.numeric??`auto`}|${e.style??`long`}`,()=>new Intl.RelativeTimeFormat(e.locale,{numeric:e.numeric??`auto`,style:e.style??`long`}))}function V(e){let t=Intl;return t.DurationFormat?N(I,`${String(e.locale??``)}|${e.style??``}`,()=>new t.DurationFormat(e.locale,{style:e.style})):null}var H=60,U=3600,W=86400,G=604800,K=2629800,q=31557600,le=[{scale:1,thresholdToPromote:H,unit:`second`},{scale:H,thresholdToPromote:U/H,unit:`minute`},{scale:U,thresholdToPromote:W/U,unit:`hour`},{scale:W,thresholdToPromote:G/W,unit:`day`},{scale:G,thresholdToPromote:K/G,unit:`week`},{scale:K,thresholdToPromote:12,unit:`month`},{scale:q,thresholdToPromote:1/0,unit:`year`}];function ue(e){Number.isFinite(e)||s(`formatRelative received a non-finite time difference.`);let t=Math.round(e);for(let{scale:e,thresholdToPromote:n,unit:r}of le){let i=Math.round(t/e);if(Math.abs(i)<n)return{unit:r,value:i}}return{unit:`year`,value:Math.round(t/q)}}var de=[`years`,`months`,`weeks`,`days`,`hours`,`minutes`,`seconds`,`milliseconds`,`microseconds`,`nanoseconds`];function fe(e){let t=[];for(let n of de){let r=Math.abs(e[n]);r!==0&&t.push(`${r} ${r===1?n.slice(0,-1):n}`)}return t.length===0?`0 seconds`:t.join(`, `)}function J(e,n,r,i){if(r.tz)return r.tz;let a=e instanceof t.Temporal.ZonedDateTime?e.timeZoneId:void 0,o=n instanceof t.Temporal.ZonedDateTime?n.timeZoneId:void 0;return a&&o&&a!==o&&s(`${i} received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`),a??o}function pe(e,n={}){let r=n.tz??(e instanceof t.Temporal.ZonedDateTime?e.timeZoneId:void 0);return z(n,r).format(new Date(m(e,{tz:r}).epochMilliseconds))}function me(e,t,n={}){let r=J(e,t,n,`formatRange`);return z(n,r).formatRange(new Date(m(e,{tz:r}).epochMilliseconds),new Date(m(t,{tz:r}).epochMilliseconds))}function he(e,t,n={}){let r=J(e,t,n,`formatRangeParts`);return z(n,r).formatRangeToParts(new Date(m(e,{tz:r}).epochMilliseconds),new Date(m(t,{tz:r}).epochMilliseconds))}function ge(e,t={}){return m(e,t).toString()}function _e(e,t={}){return h(e,{tz:l(e,t)}).toString()}function ve(e,n={}){let r=e instanceof t.Temporal.Instant?e:e.toInstant(),i=n.base?n.base instanceof t.Temporal.Instant?n.base:n.base.toInstant():t.Temporal.Now.instant(),{unit:a,value:o}=ue((r.epochMilliseconds-i.epochMilliseconds)/1e3);return B(n).format(o,a)}function Y(e){try{return t.Temporal.Duration.from(e)}catch{s(`Invalid duration input: "${String(e)}". Expected an ISO 8601 duration string or Temporal.DurationLike.`)}}function ye(e,t={}){let n=Y(e),r=V(t);return r?r.format(n):fe(n)}function be(e,n={}){let r=n.tz??(e instanceof t.Temporal.ZonedDateTime?e.timeZoneId:void 0);return z(n,r).formatToParts(new Date(m(e,{tz:r}).epochMilliseconds))}function xe(e,t={}){let{unit:n,value:r}=e;return`${t.locale?new Intl.NumberFormat(t.locale).format(r):String(r)} ${r===1?n:`${n}s`}`}var X=new WeakMap;function Se(e){let t=X.get(e);if(t)return t;let n=Object.keys(e).map(t=>({key:t,ms:Ce(e[t])})).sort((e,t)=>e.ms-t.ms);return X.set(e,n),n}function Z(e,n,r={},i=t.Temporal.Now.instant()){let a=m(e,r).epochMilliseconds-i.epochMilliseconds;for(let{key:e,ms:t}of Se(n))if(a<=t)return e;return null}function Ce(e){let n=t.Temporal.Duration.from(e);return(n.years??0)*12*p+(n.months??0)*p+(n.weeks??0)*7*864e5+(n.days??0)*864e5+(n.hours??0)*36e5+(n.minutes??0)*6e4+(n.seconds??0)*1e3+(n.milliseconds??0)+(n.microseconds??0)/1e3+(n.nanoseconds??0)/1e6}var we=[{field:`years`,unit:`year`},{field:`months`,unit:`month`},{field:`weeks`,unit:`week`},{field:`days`,unit:`day`},{field:`hours`,unit:`hour`},{field:`minutes`,unit:`minute`},{field:`seconds`,unit:`second`},{field:`milliseconds`,unit:`millisecond`}];function Q(e,n){return t.Temporal.ZonedDateTime.compare(e,n)<=0?n.since(e,{largestUnit:`year`}):e.since(n,{largestUnit:`year`})}function $(e){for(let{field:t,unit:n}of we){let r=Math.abs(e[t]);if(r>0)return{unit:n,value:r}}return{unit:`millisecond`,value:0}}function Te(e,n,r={}){let i=n??t.Temporal.Now.instant();if(!r.tz&&e instanceof t.Temporal.Instant&&i instanceof t.Temporal.Instant)return $(Q(e.toZonedDateTimeISO(`UTC`),i.toZonedDateTimeISO(`UTC`)));let a=u([e,i],r);return $(Q(h(e,{tz:a}),h(i,{tz:a})))}function Ee(e,n,i,a={}){let o=l(e,a),s=h(e,{...a,tz:o}),c=h(n,{...a,tz:o});if(t.Temporal.ZonedDateTime.compare(s.add(i),s)<=0)throw new r(`dateRange: step must advance the date forward`);return De(s,c,i)}function*De(e,n,r){let i=e;for(;t.Temporal.ZonedDateTime.compare(i,n)<=0;)yield i,i=i.add(r)}function Oe(e,t,n={}){let{count:r,frequency:i,interval:a=1,until:o}=t,s=l(e,n),c=i===`daily`?{days:a}:i===`weekly`?{weeks:a}:i===`monthly`?{months:a}:{years:a},u=o===void 0?void 0:m(o,{...n,tz:s});return ke(h(e,{...n,tz:s}),c,r,u)}function*ke(e,n,r,i){let a=e,o=0;for(;!(r!==void 0&&o>=r||i!==void 0&&t.Temporal.Instant.compare(a.toInstant(),i)>0);)yield a,o++,a=a.add(n)}return e.TempoError=n,e.TempoInvalidInputError=r,e.TempoInvalidTzError=i,e.TempoMissingTzError=a,e.TempoUnsupportedInputError=o,Object.defineProperty(e,"Temporal",{enumerable:!0,get:function(){return t.Temporal}}),e.clamp=j,e.dateRange=Ee,e.difference=S,e.endOf=re,e.expires=Z,e.format=pe,e.formatDuration=ye,e.formatInstant=ge,e.formatParts=be,e.formatRange=me,e.formatRangeParts=he,e.formatRelative=ve,e.formatZoned=_e,e.humanize=xe,e.inTz=ee,e.isAfter=oe,e.isBefore=ae,e.isSame=se,e.isValid=C,e.now=g,e.nowInstant=_,e.parse=w,e.parseDuration=Y,e.parseInstant=x,e.parsePlainDate=y,e.parsePlainDateTime=b,e.parseZoned=v,e.recurrence=Oe,e.shift=te,e.startOf=ne,e.timeDiff=Te,e.toInstant=m,e.within=ce,e})({},Temporal);
2
+ //# sourceMappingURL=tempo.iife.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tempo.iife.js","names":[],"sources":["../src/errors.ts","../src/_tz.ts","../src/_convert.ts","../src/core.ts","../src/_floor.ts","../src/boundary.ts","../src/compare.ts","../src/format.ts","../src/classify.ts","../src/range.ts"],"sourcesContent":["/** Base class for all tempo errors. Use `instanceof TempoError` to catch any tempo-originated error. */\nexport class TempoError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is TempoError {\n return err instanceof TempoError;\n }\n}\n\n/** Thrown when a date/time input string or value cannot be parsed. */\nexport class TempoInvalidInputError extends TempoError {}\n\n/** Thrown when the provided timezone identifier is unknown or invalid. */\nexport class TempoInvalidTzError extends TempoError {}\n\n/** Thrown when an operation requires a timezone but none was supplied. */\nexport class TempoMissingTzError extends TempoError {}\n\n/** Thrown when an input type is not supported by the called operation. */\nexport class TempoUnsupportedInputError extends TempoError {}\n\n// ─── Error helpers ────────────────────────────────────────────────────────────\n\ntype TempoErrorCtor = new (message: string) => TempoError;\n\nexport function fail(message: string, Class: TempoErrorCtor = TempoInvalidInputError): never {\n throw new Class(message);\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, TimeInput } from './types';\n\nimport { TempoInvalidTzError, TempoMissingTzError, fail } from './errors';\n\n// ─── Timezone validation ──────────────────────────────────────────────────────\n\nexport function validateTz(tz: string): string {\n try {\n Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(tz);\n } catch {\n fail(\n `Unknown or invalid timezone: \"${tz}\". Expected an IANA timezone name (e.g. \"America/New_York\") or UTC offset (e.g. \"+05:30\").`,\n TempoInvalidTzError,\n );\n }\n\n return tz;\n}\n\n// ─── Timezone inference ───────────────────────────────────────────────────────\n\nexport function inferTimeZone(input: TimeInput, options: { tz?: string }): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n if (!tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return validateTz(tz);\n}\n\nexport function inferSharedTimeZone(inputs: TimeInput[], options: { tz?: string }): string {\n if (options.tz) return validateTz(options.tz);\n\n let inferred: string | undefined;\n\n for (const input of inputs) {\n if (!(input instanceof Temporal.ZonedDateTime)) continue;\n\n const tz = input.timeZoneId;\n\n if (!inferred) {\n inferred = tz;\n continue;\n }\n\n if (inferred !== tz) {\n fail('Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.');\n }\n }\n\n if (!inferred)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return inferred;\n}\n\n// ─── Range normalization ──────────────────────────────────────────────────────\n\nexport function normalizeRange(start: Temporal.Instant, end: Temporal.Instant): [Temporal.Instant, Temporal.Instant] {\n return Temporal.Instant.compare(start, end) <= 0 ? [start, end] : [end, start];\n}\n\n// ─── Shared constants ─────────────────────────────────────────────────────────\n\n/** Units that require timezone-aware context for calendar-accurate operations. */\nexport const CALENDAR_UNITS = new Set<CalendarUnit>(['day', 'month', 'week', 'year']);\n\n/** Approximate millisecond constants for threshold arithmetic. */\nexport const MS_PER_MONTH = 30.4375 * 86_400_000; // 365.25 / 12 days × 86400 s × 1000\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { DateTimeDisambiguation, TimeInput } from './types';\n\nimport { validateTz } from './_tz';\nimport { TempoMissingTzError, TempoUnsupportedInputError, fail } from './errors';\n\ntype WithPrefer = { prefer?: DateTimeDisambiguation };\ntype TimeOptionsWithTz = { tz: string };\n\n// ─── Direct resolution ────────────────────────────────────────────────────────\n\n/**\n * Converts any {@link TimeInput} to an absolute `Instant`.\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n */\nexport function toInstant(input: TimeInput, options: WithPrefer & { tz?: string } = {}): Temporal.Instant {\n if (input instanceof Temporal.Instant) return input;\n\n if (input instanceof Temporal.ZonedDateTime) return input.toInstant();\n\n if (input instanceof Temporal.PlainDateTime) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input\n .toZonedDateTime(validateTz(options.tz), {\n disambiguation: options.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n })\n .toInstant();\n }\n\n if (input instanceof Temporal.PlainDate) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input.toZonedDateTime({ timeZone: validateTz(options.tz) }).toInstant();\n }\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into `options.tz` as a `ZonedDateTime`.\n *\n * When `input` is already a `ZonedDateTime`, it is **re-projected** into `options.tz`\n * via `withTimeZone()` — the wall-clock time changes but the absolute instant is preserved.\n *\n * @example\n * ```ts\n * // Re-projection: same instant, different wall-clock\n * toZoned(parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]'), { tz: 'UTC' })\n * // 2026-03-21T10:00:00+00:00[UTC] ← wall-clock changed from 11:00 → 10:00\n * ```\n */\nexport function toZoned(\n input: TimeInput,\n options: TimeOptionsWithTz | (WithPrefer & { tz: string }),\n): Temporal.ZonedDateTime {\n const opts = options as TimeOptionsWithTz & WithPrefer;\n const tz = validateTz(opts.tz);\n\n if (input instanceof Temporal.ZonedDateTime) return input.withTimeZone(tz);\n\n if (input instanceof Temporal.PlainDateTime) {\n return input.toZonedDateTime(tz, {\n disambiguation: opts.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n });\n }\n\n if (input instanceof Temporal.PlainDate) {\n return input.toZonedDateTime({ timeZone: tz });\n }\n\n if (input instanceof Temporal.Instant) return input.toZonedDateTimeISO(tz);\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into a specific timezone as a `ZonedDateTime`.\n * Unlike {@link toZoned}, this is the clean public API: explicit `tz` string parameter\n * rather than an options bag, signalling intent clearly.\n *\n * When `input` is already a `ZonedDateTime`, it is re-projected — same instant, new zone.\n *\n * @example\n * ```ts\n * inTz(parseInstant('2026-03-21T10:00:00Z'), 'Europe/Berlin')\n * // 2026-03-21T11:00:00+01:00[Europe/Berlin]\n * ```\n */\nexport function inTz(input: TimeInput, tz: string): Temporal.ZonedDateTime {\n return toZoned(input, { tz });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, DifferenceOptions, ParseAs, ShiftOptions, TimeInput } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { CALENDAR_UNITS, inferSharedTimeZone, inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\ntype TimeOptionsWithTz = { tz: string };\n\n/**\n * Returns the current date and time in the given timezone.\n *\n * @example\n * ```ts\n * now('America/New_York').hour; // current hour in New York\n * ```\n */\nexport function now(tz: string): Temporal.ZonedDateTime {\n return Temporal.Now.zonedDateTimeISO(tz);\n}\n\n/**\n * Returns the current absolute instant (UTC point in time).\n * Use this instead of `Temporal.Now.instant()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * timeDiff(nowInstant()) // { unit: 'millisecond', value: 0 } (compared to now)\n * expires(nowInstant(), { expired: { days: 0 }, safe: { years: 100 } }) // 'safe'\n * ```\n */\nexport function nowInstant(): Temporal.Instant {\n return Temporal.Now.instant();\n}\n\n/**\n * Parses a full ISO 8601 zoned date-time string into a `ZonedDateTime`.\n * Use this instead of `Temporal.ZonedDateTime.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]')\n * parseZoned('2026-03-21T00:00:00[UTC]')\n * ```\n */\nexport function parseZoned(input: string): Temporal.ZonedDateTime {\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n fail(\n `Invalid zoned date-time string: \"${input}\". Expected an ISO 8601 string with offset and timezone (e.g. 2026-03-21T10:00:00+01:00[Europe/Berlin]).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 date-only string into a timezone-free `PlainDate`.\n * Use this instead of `Temporal.PlainDate.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parsePlainDate('2026-03-21') // 2026-03-21\n * ```\n */\nexport function parsePlainDate(input: string): Temporal.PlainDate {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n fail(`Invalid plain date string: \"${input}\". Expected an ISO 8601 date string (e.g. YYYY-MM-DD).`);\n }\n}\n\n/**\n * Parses an ISO 8601 string into a timezone-free `PlainDateTime` (wall-clock time).\n * Use {@link toInstant} or {@link inTz} to attach a timezone when needed.\n *\n * @example\n * ```ts\n * parsePlainDateTime('2026-03-21') // 2026-03-21T00:00:00\n * parsePlainDateTime('2026-03-21T10:15:30') // 2026-03-21T10:15:30\n * ```\n */\nexport function parsePlainDateTime(input: string): Temporal.PlainDateTime {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n fail(\n `Invalid date/time string: \"${input}\". Expected an ISO 8601 date or date-time string (e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 UTC string into an absolute `Instant`.\n *\n * @example\n * ```ts\n * parseInstant('2026-03-21T10:15:30Z')\n * ```\n */\nexport function parseInstant(input: string): Temporal.Instant {\n try {\n return Temporal.Instant.from(input);\n } catch {\n fail(`Invalid instant string: \"${input}\". Expected an ISO 8601 UTC string (e.g. YYYY-MM-DDTHH:mm:ssZ).`);\n }\n}\n\n/**\n * DST-safe date arithmetic. Adds `duration` to `input` and returns the result as a\n * `ZonedDateTime`. Handles spring-forward and fall-back correctly.\n *\n * **Always returns a `ZonedDateTime`** — even when the input is an `Instant`.\n * Call `.toInstant()` on the result if you need an `Instant` back.\n * Requires `options.tz` when input is an `Instant`, `PlainDate`, or `PlainDateTime`.\n *\n * @example\n * ```ts\n * shift(parseZoned('2026-03-08T01:30:00-05:00[America/New_York]'), { hours: 1 })\n * // 2026-03-08T03:30:00-04:00[America/New_York] (skipped the missing hour)\n *\n * // Instant input — tz required, result is ZonedDateTime\n * shift(parseInstant('2026-03-21T10:00:00Z'), { hours: 2 }, { tz: 'UTC' }).toInstant()\n * ```\n */\nexport function shift(\n input: Temporal.ZonedDateTime,\n duration: Temporal.DurationLike,\n options?: ShiftOptions,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: Temporal.Instant | Temporal.PlainDate | Temporal.PlainDateTime,\n duration: Temporal.DurationLike,\n options: ShiftOptions & TimeOptionsWithTz,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: TimeInput,\n duration: Temporal.DurationLike,\n options: ShiftOptions = {},\n): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { prefer: options.prefer, tz }).add(duration);\n}\n\n/**\n * Returns the calendar-aware duration between `start` and `end`.\n *\n * When both inputs are `Instant` and no calendar unit is requested, the fast\n * path skips timezone conversion. Calendar units (`day`, `week`, `month`, `year`)\n * always require a timezone — pass `options.tz` or use `ZonedDateTime` inputs.\n * `options.prefer` (DST disambiguation) is only meaningful for `PlainDateTime` inputs.\n *\n * @example\n * ```ts\n * difference(\n * parseZoned('2026-03-08T00:00:00-05:00[America/New_York]'),\n * parseZoned('2026-03-09T00:00:00-04:00[America/New_York]'),\n * { largestUnit: 'hour' },\n * ).hours // 23 (DST spring-forward day)\n * ```\n */\nexport function difference(start: TimeInput, end: TimeInput, options: DifferenceOptions = {}): Temporal.Duration {\n const { largestUnit, prefer, roundingIncrement, roundingMode, smallestUnit } = options;\n const roundingOptions = { largestUnit, roundingIncrement, roundingMode, smallestUnit };\n\n const needsCalendar =\n (largestUnit !== undefined && CALENDAR_UNITS.has(largestUnit as CalendarUnit)) ||\n (smallestUnit !== undefined && CALENDAR_UNITS.has(smallestUnit as CalendarUnit));\n\n if (!needsCalendar && start instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return end.since(start, roundingOptions as Temporal.DifferenceOptions<Temporal.TimeUnit>);\n }\n\n const tz = inferSharedTimeZone([start, end], options);\n\n return toZoned(end, { prefer, tz }).since(toZoned(start, { prefer, tz }), roundingOptions);\n}\n\n/**\n * Type guard that checks whether `value` is a valid `TimeInput`.\n *\n * @example\n * ```ts\n * isValid(parseInstant('2026-03-21T10:00:00Z')) // true\n * isValid('2026-03-21') // false\n * ```\n */\nexport function isValid(value: unknown): value is TimeInput {\n return (\n value instanceof Temporal.Instant ||\n value instanceof Temporal.ZonedDateTime ||\n value instanceof Temporal.PlainDateTime ||\n value instanceof Temporal.PlainDate\n );\n}\n\n/**\n * Parses any ISO 8601 string into the most specific `TimeInput` type possible.\n * Tries ZonedDateTime → Instant → PlainDateTime → PlainDate in order.\n * Throws a descriptive `TypeError` if none match.\n *\n * Pass `as` to request a specific return type (throws if the string cannot be parsed as that type):\n *\n * @example\n * ```ts\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]') // TimeInput (auto-detect)\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]', 'zoned') // Temporal.ZonedDateTime\n * parse('2026-03-21T10:00:00Z', 'instant') // Temporal.Instant\n * parse('2026-03-21T10:00:00', 'plain-datetime') // Temporal.PlainDateTime\n * parse('2026-03-21', 'plain-date') // Temporal.PlainDate\n * ```\n */\nexport function parse(input: string, as: 'zoned'): Temporal.ZonedDateTime;\nexport function parse(input: string, as: 'instant'): Temporal.Instant;\nexport function parse(input: string, as: 'plain-datetime'): Temporal.PlainDateTime;\nexport function parse(input: string, as: 'plain-date'): Temporal.PlainDate;\nexport function parse(input: string, as?: ParseAs): TimeInput;\nexport function parse(input: string, as?: ParseAs): TimeInput {\n if (as === 'zoned') return parseZoned(input);\n\n if (as === 'instant') return parseInstant(input);\n\n if (as === 'plain-datetime') return parsePlainDateTime(input);\n\n if (as === 'plain-date') return parsePlainDate(input);\n\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n /* try next format */\n }\n\n try {\n return Temporal.Instant.from(input);\n } catch {\n /* try next format */\n }\n\n // Try PlainDateTime before PlainDate — a date-only string (no 'T') will also\n // be accepted by PlainDateTime.from(), producing midnight, so we check the\n // string to pick the most specific type.\n if (input.includes('T')) {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n /* fall through to error */\n }\n } else {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n /* fall through to error */\n }\n }\n\n fail(\n `Unable to parse date/time string: \"${input}\". Expected ISO 8601 ZonedDateTime, Instant, PlainDateTime, or PlainDate.`,\n );\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, DateTimeDisambiguation, TimeInput } from './types';\n\nimport { toZoned } from './_convert';\n\n// ─── Floor-to-boundary-unit helper ───────────────────────────────────────────\n\nconst TIME_ZERO: Temporal.ZonedDateTimeLike = {\n hour: 0,\n microsecond: 0,\n millisecond: 0,\n minute: 0,\n nanosecond: 0,\n second: 0,\n};\n\nconst BOUNDARY_CLEAR: Record<Exclude<BoundaryUnit, 'week'>, Temporal.ZonedDateTimeLike> = {\n day: TIME_ZERO,\n hour: { microsecond: 0, millisecond: 0, minute: 0, nanosecond: 0, second: 0 },\n minute: { microsecond: 0, millisecond: 0, nanosecond: 0, second: 0 },\n month: { ...TIME_ZERO, day: 1 },\n year: { ...TIME_ZERO, day: 1, month: 1 },\n};\n\n/**\n * Floors `input` to the start of `unit` in `tz`, returning an `Instant`.\n * Used internally by both `boundary.ts` and `compare.ts` without either depending on the other.\n */\nexport function floorToUnit(\n input: TimeInput,\n unit: BoundaryUnit,\n options: { prefer?: DateTimeDisambiguation; tz: string; weekStartsOn?: number },\n): Temporal.Instant {\n const zoned = toZoned(input, { prefer: options.prefer, tz: options.tz });\n\n if (unit === 'week') {\n const daysToSubtract = (zoned.dayOfWeek - (options.weekStartsOn ?? 1) + 7) % 7;\n\n return zoned.subtract({ days: daysToSubtract }).with(TIME_ZERO).toInstant();\n }\n\n return zoned.with(BOUNDARY_CLEAR[unit]).toInstant();\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryOptions, BoundaryUnit, TimeInput } from './types';\n\nimport { floorToUnit } from './_floor';\nimport { inferTimeZone } from './_tz';\n\n// ─── Boundary step durations ──────────────────────────────────────────────────\n\nconst BOUNDARY_STEP: Record<BoundaryUnit, Temporal.DurationLike> = {\n day: { days: 1 },\n hour: { hours: 1 },\n minute: { minutes: 1 },\n month: { months: 1 },\n week: { weeks: 1 },\n year: { years: 1 },\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns the start of the given `unit` in the inferred or explicit timezone.\n *\n * @example\n * ```ts\n * startOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T00:00:00+00:00[UTC]\n *\n * startOf(instant, 'week', { tz: 'UTC', weekStartsOn: 1 })\n * // Monday of the current week\n * ```\n */\nexport function startOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn }).toZonedDateTimeISO(tz);\n}\n\n/**\n * Returns the last nanosecond of the given `unit` (exactly 1 ns before the next unit starts).\n *\n * @example\n * ```ts\n * endOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T23:59:59.999999999+00:00[UTC]\n * ```\n */\nexport function endOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n const startInstant = floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn });\n\n return startInstant.toZonedDateTimeISO(tz).add(BOUNDARY_STEP[unit]).subtract({ nanoseconds: 1 });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, CompareOptions, TimeInput } from './types';\n\nimport { toInstant } from './_convert';\nimport { floorToUnit } from './_floor';\nimport { inferSharedTimeZone, normalizeRange } from './_tz';\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveFlooredPair(\n a: TimeInput,\n b: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { left: Temporal.Instant; right: Temporal.Instant } {\n const tz = inferSharedTimeZone([a, b], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n\n return {\n left: floorToUnit(a, unit, unitOpts),\n right: floorToUnit(b, unit, unitOpts),\n };\n}\n\nfunction resolveFlooredTriple(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { lower: Temporal.Instant; target: Temporal.Instant; upper: Temporal.Instant } {\n const tz = inferSharedTimeZone([value, start, end], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n const target = floorToUnit(value, unit, unitOpts);\n const [lower, upper] = normalizeRange(floorToUnit(start, unit, unitOpts), floorToUnit(end, unit, unitOpts));\n\n return { lower, target, upper };\n}\n\nfunction compareByUnit(a: TimeInput, b: TimeInput, options: CompareOptions): number {\n if (!options.unit) {\n return Temporal.Instant.compare(toInstant(a, options), toInstant(b, options));\n }\n\n const { left, right } = resolveFlooredPair(a, b, options.unit, options);\n\n return Temporal.Instant.compare(left, right);\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when `a` is strictly before `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isBefore(parseInstant('2026-03-21T10:00:00Z'), parseInstant('2026-03-21T11:00:00Z'))\n * // true\n * ```\n */\nexport function isBefore(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) < 0;\n}\n\n/**\n * Returns `true` when `a` is strictly after `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isAfter(parseInstant('2026-03-21T11:00:00Z'), parseInstant('2026-03-21T10:00:00Z'))\n * // true\n * ```\n */\nexport function isAfter(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) > 0;\n}\n\n/**\n * Returns `true` when `a` and `b` represent the same point (or boundary unit) in time.\n *\n * @example\n * ```ts\n * isSame(a, b, { tz: 'America/New_York', unit: 'day' })\n * // true when a and b fall on the same calendar day in New York\n * ```\n */\nexport function isSame(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) === 0;\n}\n\n/**\n * Returns `true` when `value` falls within `[start, end]` (inclusive, bounds normalized).\n * Pass `options.unit` to floor all three inputs to a calendar boundary before comparing.\n *\n * @example\n * ```ts\n * within(\n * parseInstant('2026-03-21T11:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ) // true\n * ```\n */\nexport function within(value: TimeInput, start: TimeInput, end: TimeInput, options: CompareOptions = {}): boolean {\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n}\n\n/**\n * Clamps `value` to within `[start, end]` (bounds normalized).\n *\n * When `value` is a `ZonedDateTime`, returns a `ZonedDateTime` in the same timezone.\n * Otherwise returns an `Instant`.\n *\n * When `options.unit` is set, all three inputs are floored to that calendar boundary before\n * clamping — the result is at the start of the boundary unit, not the original time-of-day.\n *\n * @example\n * ```ts\n * clamp(\n * parseInstant('2026-03-21T13:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ).toString() // '2026-03-21T12:00:00Z'\n *\n * clamp(\n * parseZoned('2026-03-21T13:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T10:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T12:00:00+00:00[UTC]'),\n * ).toString() // '2026-03-21T12:00:00+00:00[UTC]'\n * ```\n */\nexport function clamp(\n value: Temporal.ZonedDateTime,\n start: TimeInput,\n end: TimeInput,\n options?: CompareOptions,\n): Temporal.ZonedDateTime;\nexport function clamp(value: TimeInput, start: TimeInput, end: TimeInput, options?: CompareOptions): Temporal.Instant;\nexport function clamp(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n options: CompareOptions = {},\n): Temporal.Instant | Temporal.ZonedDateTime {\n const isZoned = value instanceof Temporal.ZonedDateTime;\n const tz = isZoned ? value.timeZoneId : undefined;\n\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n return isZoned && tz ? clamped.toZonedDateTimeISO(tz) : clamped;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n // For unit-based clamping, resolve the output tz from options or from value's zone\n const outTz = options.tz ?? tz ?? inferSharedTimeZone([value, start, end], options);\n\n return isZoned ? clamped.toZonedDateTimeISO(outTz) : clamped;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type {\n DurationFormatOptions,\n FormatOptions,\n FormatPattern,\n RelativeFormatOptions,\n RelativeTimeInput,\n TimeDiffResult,\n TimeInput,\n TimeOptions,\n} from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\n// ─── Formatter types ──────────────────────────────────────────────────────────\n\ntype DurationFormatter = { format(value: Temporal.Duration): string };\ntype DurationFormatterConstructor = new (\n locales?: Intl.LocalesArgument,\n options?: { style?: 'digital' | 'long' | 'narrow' | 'short' },\n) => DurationFormatter;\n\n// ─── Formatter caches ─────────────────────────────────────────────────────────\n\nconst FORMATTER_CACHE_MAX = 128;\n\nfunction cappedGetOrCreate<V>(cache: Map<string, V>, key: string, factory: () => V): V {\n const cached = cache.get(key);\n\n if (cached !== undefined) return cached;\n\n if (cache.size >= FORMATTER_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n\n if (oldest !== undefined) cache.delete(oldest);\n }\n\n const value = factory();\n\n cache.set(key, value);\n\n return value;\n}\n\nconst DATE_TIME_FORMATTER_CACHE = new Map<string, Intl.DateTimeFormat>();\nconst RELATIVE_TIME_FORMATTER_CACHE = new Map<string, Intl.RelativeTimeFormat>();\nconst DURATION_FORMATTER_CACHE = new Map<string, DurationFormatter>();\n\n// ─── Format presets ───────────────────────────────────────────────────────────\n\nconst FORMAT_PRESETS: Record<FormatPattern, Intl.DateTimeFormatOptions> = {\n 'date-only': { dateStyle: 'short' },\n long: { dateStyle: 'full', timeStyle: 'long' },\n medium: { dateStyle: 'medium', timeStyle: 'short' },\n short: { dateStyle: 'short', timeStyle: 'short' },\n 'time-only': { timeStyle: 'short' },\n};\n\n// ─── Formatter factory helpers ────────────────────────────────────────────────\n\nfunction serializeIntlOptions(options: Intl.DateTimeFormatOptions): string {\n return JSON.stringify(\n Object.entries(options)\n .filter(([, value]) => value !== undefined)\n .sort(([l], [r]) => l.localeCompare(r))\n .map(([key, value]) => [key, String(value)]),\n );\n}\n\nfunction makeFormatter(options: FormatOptions, fallbackTz?: string): Intl.DateTimeFormat {\n const tz = options.tz ?? fallbackTz;\n const locale = options.locale;\n\n if (options.intl !== undefined) {\n const cacheKey = `${String(locale ?? '')}|intl|${tz ?? ''}|${serializeIntlOptions(options.intl)}`;\n\n return cappedGetOrCreate(DATE_TIME_FORMATTER_CACHE, cacheKey, () => {\n const intlOptions = tz !== undefined ? { ...options.intl, timeZone: tz } : options.intl;\n\n return new Intl.DateTimeFormat(locale, intlOptions);\n });\n }\n\n const pattern = options.pattern ?? 'medium';\n const cacheKey = `${String(locale ?? '')}|${pattern}|${tz ?? ''}`;\n\n return cappedGetOrCreate(\n DATE_TIME_FORMATTER_CACHE,\n cacheKey,\n () => new Intl.DateTimeFormat(locale, { ...FORMAT_PRESETS[pattern], timeZone: tz }),\n );\n}\n\nfunction getRelativeFormatter(options: {\n locale?: Intl.LocalesArgument;\n numeric?: Intl.RelativeTimeFormatNumeric;\n style?: Intl.RelativeTimeFormatStyle;\n}): Intl.RelativeTimeFormat {\n const cacheKey = `${String(options.locale ?? '')}|${options.numeric ?? 'auto'}|${options.style ?? 'long'}`;\n\n return cappedGetOrCreate(\n RELATIVE_TIME_FORMATTER_CACHE,\n cacheKey,\n () =>\n new Intl.RelativeTimeFormat(options.locale, {\n numeric: options.numeric ?? 'auto',\n style: options.style ?? 'long',\n }),\n );\n}\n\nfunction getDurationFormatter(options: {\n locale?: Intl.LocalesArgument;\n style?: 'digital' | 'long' | 'narrow' | 'short';\n}): DurationFormatter | null {\n const IntlWithDurationFormat = Intl as typeof Intl & { DurationFormat?: DurationFormatterConstructor };\n\n if (!IntlWithDurationFormat.DurationFormat) return null;\n\n const cacheKey = `${String(options.locale ?? '')}|${options.style ?? ''}`;\n\n return cappedGetOrCreate(\n DURATION_FORMATTER_CACHE,\n cacheKey,\n () => new IntlWithDurationFormat.DurationFormat!(options.locale, { style: options.style }),\n );\n}\n\n// ─── Time scale constants ─────────────────────────────────────────────────────\n\nconst SECONDS_PER_MINUTE = 60;\nconst SECONDS_PER_HOUR = 3_600;\nconst SECONDS_PER_DAY = 86_400;\nconst SECONDS_PER_WEEK = 604_800;\nconst SECONDS_PER_MONTH = 2_629_800; // ≈ 30.4375 days × 86400\nconst SECONDS_PER_YEAR = 31_557_600; // 365.25 days × 86400\n\n// ─── Relative time helpers ────────────────────────────────────────────────────\n\nconst RELATIVE_UNITS: ReadonlyArray<{ scale: number; thresholdToPromote: number; unit: Intl.RelativeTimeFormatUnit }> =\n [\n { scale: 1, thresholdToPromote: SECONDS_PER_MINUTE, unit: 'second' },\n { scale: SECONDS_PER_MINUTE, thresholdToPromote: SECONDS_PER_HOUR / SECONDS_PER_MINUTE, unit: 'minute' },\n { scale: SECONDS_PER_HOUR, thresholdToPromote: SECONDS_PER_DAY / SECONDS_PER_HOUR, unit: 'hour' },\n { scale: SECONDS_PER_DAY, thresholdToPromote: SECONDS_PER_WEEK / SECONDS_PER_DAY, unit: 'day' },\n { scale: SECONDS_PER_WEEK, thresholdToPromote: SECONDS_PER_MONTH / SECONDS_PER_WEEK, unit: 'week' },\n { scale: SECONDS_PER_MONTH, thresholdToPromote: 12, unit: 'month' },\n { scale: SECONDS_PER_YEAR, thresholdToPromote: Number.POSITIVE_INFINITY, unit: 'year' },\n ];\n\nfunction toRelativeUnit(seconds: number): { unit: Intl.RelativeTimeFormatUnit; value: number } {\n if (!Number.isFinite(seconds)) fail('formatRelative received a non-finite time difference.');\n\n const roundedSeconds = Math.round(seconds);\n\n for (const { scale, thresholdToPromote, unit } of RELATIVE_UNITS) {\n const value = Math.round(roundedSeconds / scale);\n\n if (Math.abs(value) < thresholdToPromote) return { unit, value };\n }\n\n return { unit: 'year', value: Math.round(roundedSeconds / SECONDS_PER_YEAR) };\n}\n\n// ─── Duration fallback renderer ───────────────────────────────────────────────\n\n// All English duration unit names follow the same pluralization rule: singular = plural.slice(0, -1)\nconst DURATION_UNITS = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n] as const satisfies ReadonlyArray<keyof Temporal.Duration>;\n\n// English-only fallback; runs only when Intl.DurationFormat is unavailable in the runtime.\nfunction buildDurationFallback(duration: Temporal.Duration): string {\n const parts: string[] = [];\n\n for (const unit of DURATION_UNITS) {\n const value = Math.abs(duration[unit] as number);\n\n if (value !== 0) parts.push(`${value} ${value === 1 ? unit.slice(0, -1) : unit}`);\n }\n\n return parts.length === 0 ? '0 seconds' : parts.join(', ');\n}\n\n// ─── Private helpers ──────────────────────────────────────────────────────────\n\n/**\n * Resolves a shared display timezone for two-input range functions.\n * Throws when both inputs are `ZonedDateTime` with different zones and no `options.tz` override.\n */\nfunction resolveRangeTz(start: TimeInput, end: TimeInput, options: FormatOptions, caller: string): string | undefined {\n if (options.tz) return options.tz;\n\n const startTz = start instanceof Temporal.ZonedDateTime ? start.timeZoneId : undefined;\n const endTz = end instanceof Temporal.ZonedDateTime ? end.timeZoneId : undefined;\n\n if (startTz && endTz && startTz !== endTz) {\n fail(`${caller} received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`);\n }\n\n return startTz ?? endTz;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Formats `input` using `Intl.DateTimeFormat`. Defaults to `pattern: 'medium'`.\n *\n * Pass `intl` for full `Intl.DateTimeFormatOptions` control (mutually exclusive with `pattern`).\n * The timezone is inferred from a `ZonedDateTime` input or from `options.tz`.\n *\n * @example\n * ```ts\n * format(parseInstant('2026-03-21T10:15:30Z'), { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:15'\n * ```\n */\nexport function format(input: TimeInput, options: FormatOptions = {}): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).format(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Formats a time span between `start` and `end` using `Intl.DateTimeFormat.formatRange`.\n *\n * @example\n * ```ts\n * formatRange(start, end, { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:00 – 12:00'\n * ```\n */\nexport function formatRange(start: TimeInput, end: TimeInput, options: FormatOptions = {}): string {\n const tz = resolveRangeTz(start, end, options, 'formatRange');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRange(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Returns the raw `Intl.DateTimeRangeFormatPart[]` array for a time span, enabling\n * fine-grained rendering of range start, end, and shared parts separately.\n *\n * @example\n * ```ts\n * formatRangeParts(start, end, { locale: 'en-US', pattern: 'short', tz: 'UTC' })\n * // [{ type: 'month', value: '3', source: 'startRange' }, ...]\n * ```\n */\nexport function formatRangeParts(\n start: TimeInput,\n end: TimeInput,\n options: FormatOptions = {},\n): ReturnType<Intl.DateTimeFormat['formatRangeToParts']> {\n const tz = resolveRangeTz(start, end, options, 'formatRangeParts');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRangeToParts(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Serializes `input` to a UTC ISO 8601 instant string (`2026-03-21T10:15:30Z`).\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * @example\n * ```ts\n * formatInstant(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T10:15:30Z'\n * ```\n */\nexport function formatInstant(input: TimeInput, options: TimeOptions = {}): string {\n return toInstant(input, options).toString();\n}\n\n/**\n * Serializes `input` to a zoned ISO 8601 string (`2026-03-21T11:15:30+01:00[Europe/Berlin]`).\n *\n * @param options.tz - Required when `input` is a `PlainDate` or `PlainDateTime`.\n * Inferred automatically from a `ZonedDateTime` or `Instant` input.\n *\n * @throws {TempoError} When `input` is a `PlainDate` or `PlainDateTime` and `options.tz` is omitted.\n *\n * @example\n * ```ts\n * formatZoned(parseInstant('2026-03-21T10:15:30Z'), { tz: 'Europe/Berlin' })\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]'\n *\n * formatZoned(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]' (tz inferred)\n * ```\n */\nexport function formatZoned(input: TimeInput, options: TimeOptions = {}): string {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { tz }).toString();\n}\n\n/**\n * Formats `input` relative to `options.base` (defaults to now) using `Intl.RelativeTimeFormat`.\n *\n * @example\n * ```ts\n * formatRelative(parseInstant('2026-03-21T12:00:00Z'), {\n * base: parseInstant('2026-03-21T10:00:00Z'),\n * locale: 'en-US',\n * numeric: 'always',\n * })\n * // 'in 2 hours'\n * ```\n */\nexport function formatRelative(input: RelativeTimeInput, options: RelativeFormatOptions = {}): string {\n const target = input instanceof Temporal.Instant ? input : input.toInstant();\n const base = options.base\n ? options.base instanceof Temporal.Instant\n ? options.base\n : options.base.toInstant()\n : Temporal.Now.instant();\n const differenceInSeconds = (target.epochMilliseconds - base.epochMilliseconds) / 1000;\n const { unit, value } = toRelativeUnit(differenceInSeconds);\n\n return getRelativeFormatter(options).format(value, unit);\n}\n\n/**\n * Parses an ISO duration string or `Temporal.DurationLike` into a `Temporal.Duration`.\n *\n * @example\n * ```ts\n * parseDuration('PT2H30M').toString() // 'PT2H30M'\n * parseDuration({ hours: 2, minutes: 30 }).toString() // 'PT2H30M'\n * ```\n */\nexport function parseDuration(input: string | Temporal.DurationLike): Temporal.Duration {\n try {\n return Temporal.Duration.from(input);\n } catch {\n fail(`Invalid duration input: \"${String(input)}\". Expected an ISO 8601 duration string or Temporal.DurationLike.`);\n }\n}\n\n/**\n * Formats a duration using `Intl.DurationFormat` when available, falling back to\n * a human-readable plain-English string.\n *\n * @example\n * ```ts\n * formatDuration('PT2H30M', { locale: 'en-US', style: 'long' })\n * // '2 hours, 30 minutes'\n * ```\n */\nexport function formatDuration(input: string | Temporal.DurationLike, options: DurationFormatOptions = {}): string {\n const duration = parseDuration(input);\n const formatter = getDurationFormatter(options);\n\n if (formatter) return formatter.format(duration);\n\n return buildDurationFallback(duration);\n}\n\n/**\n * Returns the raw `Intl.DateTimeFormatPart[]` array for `input`, enabling\n * custom rendering where individual parts (year, month, day, etc.) need\n * to be styled or composed differently.\n *\n * @example\n * ```ts\n * formatParts(parseInstant('2026-03-21T10:15:30Z'), { pattern: 'medium', tz: 'UTC' })\n * // [{ type: 'month', value: 'Mar' }, { type: 'literal', value: ' ' }, ...]\n * ```\n */\nexport function formatParts(input: TimeInput, options: FormatOptions = {}): Intl.DateTimeFormatPart[] {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).formatToParts(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Converts a `TimeDiffResult` to a human-readable string.\n * Uses the singular unit name when value is 1, plural (unit + 's') otherwise.\n *\n * Pass `options.locale` to localize the numeric part via `Intl.NumberFormat`.\n * Unit names remain English — for fully localized output use {@link formatRelative}\n * or {@link formatDuration} instead.\n *\n * @example\n * ```ts\n * humanize({ unit: 'day', value: 1 }) // '1 day'\n * humanize({ unit: 'day', value: 3 }) // '3 days'\n * humanize({ unit: 'day', value: 3 }, { locale: 'ar' }) // '٣ days'\n * humanize({ unit: 'millisecond', value: 0 }) // '0 milliseconds'\n * ```\n */\nexport function humanize(diff: TimeDiffResult, options: { locale?: Intl.LocalesArgument } = {}): string {\n const { unit, value } = diff;\n const formatted = options.locale ? new Intl.NumberFormat(options.locale).format(value) : String(value);\n\n return `${formatted} ${value === 1 ? unit : `${unit}s`}`;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { TimeDiffResult, TimeDiffUnit, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferSharedTimeZone, MS_PER_MONTH } from './_tz';\n\n// ─── Threshold sort cache ─────────────────────────────────────────────────────\n\ntype SortedThreshold<K extends string> = { key: K; ms: number }[];\n\nconst THRESHOLD_SORT_CACHE = new WeakMap<object, SortedThreshold<string>>();\n\nfunction getSortedThresholds<K extends string>(thresholds: Record<K, Temporal.DurationLike>): SortedThreshold<K> {\n const cached = THRESHOLD_SORT_CACHE.get(thresholds);\n\n if (cached) return cached as SortedThreshold<K>;\n\n const sorted = (Object.keys(thresholds) as K[])\n .map((key) => ({ key, ms: durationToMs(thresholds[key]) }))\n .sort((a, b) => a.ms - b.ms);\n\n THRESHOLD_SORT_CACHE.set(thresholds, sorted);\n\n return sorted;\n}\n\n// ─── expires ─────────────────────────────────────────────────────────────────\n\n/**\n * Classifies a date into a user-defined bucket by comparing diff = date − now\n * against the provided thresholds (sorted ascending). Returns the key of the\n * first threshold the diff falls within, or `null` if no threshold matches.\n *\n * Thresholds accept negative durations to classify past dates. The function\n * requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * **Performance:** threshold objects are cached by reference in a `WeakMap`. Define\n * the threshold record at module scope (not inline) so sorting is performed only once\n * per unique object.\n *\n * @example\n * ```ts\n * expires(expiresAt, {\n * longExpired: { days: -30 }, // more than 30 days in the past\n * expired: { days: 0 }, // any past date\n * critical: { days: 3 }, // within 3 days\n * warning: { days: 14 }, // within 14 days\n * safe: { years: 100 }, // catch-all for far future\n * })\n * // → 'longExpired' | 'expired' | 'critical' | 'warning' | 'safe' | null\n * ```\n */\nexport function expires<K extends string>(\n date: TimeInput,\n thresholds: Record<K, Temporal.DurationLike>,\n options: TimeOptions = {},\n now = Temporal.Now.instant(),\n): K | null {\n const dateMs = toInstant(date, options).epochMilliseconds;\n const nowMs = now.epochMilliseconds;\n\n // diff is positive for future dates, negative for past dates (date − now)\n const diffMs = dateMs - nowMs;\n\n for (const { key, ms } of getSortedThresholds(thresholds)) {\n if (diffMs <= ms) return key;\n }\n\n return null;\n}\n\n/** Converts a `DurationLike` to approximate milliseconds for threshold comparison. */\nfunction durationToMs(duration: Temporal.DurationLike): number {\n const d = Temporal.Duration.from(duration);\n\n // Use approximate conversions — thresholds are human-defined boundaries, not calendar-precise.\n return (\n (d.years ?? 0) * 12 * MS_PER_MONTH +\n (d.months ?? 0) * MS_PER_MONTH +\n (d.weeks ?? 0) * 7 * 86_400_000 +\n (d.days ?? 0) * 86_400_000 +\n (d.hours ?? 0) * 3_600_000 +\n (d.minutes ?? 0) * 60_000 +\n (d.seconds ?? 0) * 1_000 +\n (d.milliseconds ?? 0) +\n (d.microseconds ?? 0) / 1_000 +\n (d.nanoseconds ?? 0) / 1_000_000\n );\n}\n\n// ─── timeDiff ─────────────────────────────────────────────────────────────────\n\nconst UNIT_ORDER: ReadonlyArray<{ field: keyof Temporal.Duration; unit: TimeDiffUnit }> = [\n { field: 'years', unit: 'year' },\n { field: 'months', unit: 'month' },\n { field: 'weeks', unit: 'week' },\n { field: 'days', unit: 'day' },\n { field: 'hours', unit: 'hour' },\n { field: 'minutes', unit: 'minute' },\n { field: 'seconds', unit: 'second' },\n { field: 'milliseconds', unit: 'millisecond' },\n];\n\nfunction sinceZoned(a: Temporal.ZonedDateTime, b: Temporal.ZonedDateTime): Temporal.Duration {\n return Temporal.ZonedDateTime.compare(a, b) <= 0\n ? b.since(a, { largestUnit: 'year' })\n : a.since(b, { largestUnit: 'year' });\n}\n\nfunction pickLargestUnit(duration: Temporal.Duration): TimeDiffResult {\n for (const { field, unit } of UNIT_ORDER) {\n const value = Math.abs(duration[field] as number);\n\n if (value > 0) return { unit, value };\n }\n\n return { unit: 'millisecond', value: 0 };\n}\n\n/**\n * Returns the absolute calendar-accurate difference between two dates as a\n * structured `{ unit, value }` in the largest meaningful unit.\n *\n * When `b` is omitted, the current instant is used.\n * Requires `options.tz` when inputs are `PlainDate`, `PlainDateTime`, or plain `Instant` with\n * calendar-unit precision. Throws when timezone cannot be inferred from inputs.\n *\n * @example\n * ```ts\n * timeDiff(\n * parseInstant('2026-01-01T00:00:00Z'),\n * parseInstant('2027-03-15T00:00:00Z'),\n * )\n * // { unit: 'year', value: 1 }\n * ```\n */\nexport function timeDiff(a: TimeInput, b?: TimeInput, options: TimeOptions = {}): TimeDiffResult {\n const end: TimeInput = b ?? Temporal.Now.instant();\n\n // Fast path: two Instants with no explicit tz — project to UTC for calendar-accurate units.\n // Instants are absolute and timezone-independent; UTC is the canonical calendar context.\n if (!options.tz && a instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return pickLargestUnit(sinceZoned(a.toZonedDateTimeISO('UTC'), end.toZonedDateTimeISO('UTC')));\n }\n\n // Plain inputs or calendar-accurate comparison require a timezone.\n const tz = inferSharedTimeZone([a, end], options);\n\n return pickLargestUnit(sinceZoned(toZoned(a, { tz }), toZoned(end, { tz })));\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { RecurrenceRule, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { TempoInvalidInputError } from './errors';\n\n/**\n * Lazily generates `ZonedDateTime` values between `start` and `end` (inclusive),\n * advancing by `step` on each iteration.\n *\n * Returns a generator — use `for...of` for lazy consumption or spread to collect\n * into an array: `[...dateRange(...)]`.\n *\n * @throws {RangeError} when `step` does not advance the date forward. Thrown eagerly at call time.\n *\n * Yields nothing when `start > end` (the generator terminates immediately).\n *\n * When `start` is a `ZonedDateTime`, the timezone is inferred from it. If `end` is in a\n * different timezone, it is silently re-projected into `start`'s timezone. Pass `options.tz`\n * explicitly to override.\n *\n * @example\n * ```ts\n * // Lazy — safe for large ranges\n * for (const day of dateRange(start, end, { days: 1 }, { tz: 'UTC' })) {\n * if (someCondition(day)) break;\n * }\n *\n * // Collect to array\n * const days = [...dateRange(start, end, { days: 1 }, { tz: 'UTC' })];\n *\n * // ZonedDateTime inputs — tz is inferred, no need to pass options\n * const days = [...dateRange(zdtStart, zdtEnd, { days: 1 })];\n * ```\n */\nexport function dateRange(\n start: TimeInput,\n end: TimeInput,\n step: Temporal.DurationLike,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const tz = inferTimeZone(start, options);\n const startZoned = toZoned(start, { ...options, tz });\n const endZoned = toZoned(end, { ...options, tz });\n\n // Eager validation — fires at call time, not on first iteration.\n if (Temporal.ZonedDateTime.compare(startZoned.add(step), startZoned) <= 0) {\n throw new TempoInvalidInputError('dateRange: step must advance the date forward');\n }\n\n return dateRangeGenerator(startZoned, endZoned, step);\n}\n\nfunction* dateRangeGenerator(\n start: Temporal.ZonedDateTime,\n end: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n\n while (Temporal.ZonedDateTime.compare(current, end) <= 0) {\n yield current;\n current = current.add(step);\n }\n}\n\n/**\n * Lazily generates `ZonedDateTime` occurrences according to a recurrence rule.\n *\n * Supports `daily`, `weekly`, `monthly`, and `yearly` frequencies with an optional\n * `interval` (defaults to `1`), `count` limit, and `until` boundary (inclusive).\n * The `RecurrenceRule` type enforces that at least one of `count` or `until` must be\n * provided — this is a compile-time guarantee for TypeScript callers.\n * Passing `count: 0` yields an empty sequence without error.\n *\n * @example\n * ```ts\n * // Every Monday for 4 weeks\n * const mondays = [...recurrence(start, { frequency: 'weekly', count: 4 }, { tz: 'UTC' })];\n *\n * // Bi-weekly until a deadline\n * for (const date of recurrence(start, { frequency: 'weekly', interval: 2, until: deadline }, { tz: 'UTC' })) {\n * schedule(date);\n * }\n *\n * // ZonedDateTime start — tz is inferred, no need to pass options\n * for (const date of recurrence(zdtStart, { frequency: 'daily', count: 7 })) {\n * schedule(date);\n * }\n * ```\n */\nexport function recurrence(\n start: TimeInput,\n rule: RecurrenceRule,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const { count, frequency, interval = 1, until } = rule;\n\n const tz = inferTimeZone(start, options);\n\n const step: Temporal.DurationLike =\n frequency === 'daily'\n ? { days: interval }\n : frequency === 'weekly'\n ? { weeks: interval }\n : frequency === 'monthly'\n ? { months: interval }\n : { years: interval };\n\n const endInstant = until !== undefined ? toInstant(until, { ...options, tz }) : undefined;\n\n return recurrenceGenerator(toZoned(start, { ...options, tz }), step, count, endInstant);\n}\n\nfunction* recurrenceGenerator(\n start: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n count: number | undefined,\n endInstant: Temporal.Instant | undefined,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n let emitted = 0;\n\n while (true) {\n if (count !== undefined && emitted >= count) break;\n\n if (endInstant !== undefined && Temporal.Instant.compare(current.toInstant(), endInstant) > 0) break;\n\n yield current;\n emitted++;\n current = current.add(step);\n }\n}\n"],"mappings":"sFACA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAA4C,CAAW,CAAC,EAG3C,EAAb,cAAyC,CAAW,CAAC,EAGxC,EAAb,cAAyC,CAAW,CAAC,EAGxC,EAAb,cAAgD,CAAW,CAAC,EAM5D,SAAgB,EAAK,EAAiB,EAAwB,EAA+B,CAC3F,MAAM,IAAI,EAAM,CAAO,CACzB,CCvBA,SAAgB,EAAW,EAAoB,CAC7C,GAAI,CACF,EAAA,SAAS,QAAQ,sBAAsB,CAAC,CAAC,CAAC,mBAAmB,CAAE,CACjE,MAAQ,CACN,EACE,iCAAiC,EAAG,4FACpC,CACF,CACF,CAEA,OAAO,CACT,CAIA,SAAgB,EAAc,EAAkB,EAAkC,CAChF,IAAM,EAAK,EAAQ,KAAO,aAAiB,EAAA,SAAS,cAAgB,EAAM,WAAa,IAAA,IAKvF,OAHK,GACH,EAAK,oFAAqF,CAAmB,EAExG,EAAW,CAAE,CACtB,CAEA,SAAgB,EAAoB,EAAqB,EAAkC,CACzF,GAAI,EAAQ,GAAI,OAAO,EAAW,EAAQ,EAAE,EAE5C,IAAI,EAEJ,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,EAAE,aAAiB,EAAA,SAAS,eAAgB,SAEhD,IAAM,EAAK,EAAM,WAEjB,GAAI,CAAC,EAAU,CACb,EAAW,EACX,QACF,CAEI,IAAa,GACf,EAAK,iGAAiG,CAE1G,CAKA,OAHK,GACH,EAAK,oFAAqF,CAAmB,EAExG,CACT,CAIA,SAAgB,EAAe,EAAyB,EAA6D,CACnH,OAAO,EAAA,SAAS,QAAQ,QAAQ,EAAO,CAAG,GAAK,EAAI,CAAC,EAAO,CAAG,EAAI,CAAC,EAAK,CAAK,CAC/E,CAKA,IAAa,EAAiB,IAAI,IAAkB,CAAC,MAAO,QAAS,OAAQ,MAAM,CAAC,EAGvE,EAAe,QAAU,MCtDtC,SAAgB,EAAU,EAAkB,EAAwC,CAAC,EAAqB,CACxG,GAAI,aAAiB,EAAA,SAAS,QAAS,OAAO,EAE9C,GAAI,aAAiB,EAAA,SAAS,cAAe,OAAO,EAAM,UAAU,EAEpE,GAAI,aAAiB,EAAA,SAAS,cAI5B,OAHK,EAAQ,IACX,EAAK,oFAAqF,CAAmB,EAExG,EACJ,gBAAgB,EAAW,EAAQ,EAAE,EAAG,CACvC,eAAgB,EAAQ,MAC1B,CAAC,CAAC,CACD,UAAU,EAGf,GAAI,aAAiB,EAAA,SAAS,UAI5B,OAHK,EAAQ,IACX,EAAK,oFAAqF,CAAmB,EAExG,EAAM,gBAAgB,CAAE,SAAU,EAAW,EAAQ,EAAE,CAAE,CAAC,CAAC,CAAC,UAAU,EAG/E,EAAK,gCAAgC,OAAO,CAAK,IAAK,CAA0B,CAClF,CAeA,SAAgB,EACd,EACA,EACwB,CACxB,IAAM,EAAO,EACP,EAAK,EAAW,EAAK,EAAE,EAE7B,GAAI,aAAiB,EAAA,SAAS,cAAe,OAAO,EAAM,aAAa,CAAE,EAEzE,GAAI,aAAiB,EAAA,SAAS,cAC5B,OAAO,EAAM,gBAAgB,EAAI,CAC/B,eAAgB,EAAK,MACvB,CAAC,EAGH,GAAI,aAAiB,EAAA,SAAS,UAC5B,OAAO,EAAM,gBAAgB,CAAE,SAAU,CAAG,CAAC,EAG/C,GAAI,aAAiB,EAAA,SAAS,QAAS,OAAO,EAAM,mBAAmB,CAAE,EAEzE,EAAK,gCAAgC,OAAO,CAAK,IAAK,CAA0B,CAClF,CAeA,SAAgB,GAAK,EAAkB,EAAoC,CACzE,OAAO,EAAQ,EAAO,CAAE,IAAG,CAAC,CAC9B,CC5EA,SAAgB,EAAI,EAAoC,CACtD,OAAO,EAAA,SAAS,IAAI,iBAAiB,CAAE,CACzC,CAYA,SAAgB,GAA+B,CAC7C,OAAO,EAAA,SAAS,IAAI,QAAQ,CAC9B,CAYA,SAAgB,EAAW,EAAuC,CAChE,GAAI,CACF,OAAO,EAAA,SAAS,cAAc,KAAK,CAAK,CAC1C,MAAQ,CACN,EACE,oCAAoC,EAAM,yGAC5C,CACF,CACF,CAWA,SAAgB,EAAe,EAAmC,CAChE,GAAI,CACF,OAAO,EAAA,SAAS,UAAU,KAAK,CAAK,CACtC,MAAQ,CACN,EAAK,+BAA+B,EAAM,uDAAuD,CACnG,CACF,CAYA,SAAgB,EAAmB,EAAuC,CACxE,GAAI,CACF,OAAO,EAAA,SAAS,cAAc,KAAK,CAAK,CAC1C,MAAQ,CACN,EACE,8BAA8B,EAAM,2FACtC,CACF,CACF,CAUA,SAAgB,EAAa,EAAiC,CAC5D,GAAI,CACF,OAAO,EAAA,SAAS,QAAQ,KAAK,CAAK,CACpC,MAAQ,CACN,EAAK,4BAA4B,EAAM,gEAAgE,CACzG,CACF,CA6BA,SAAgB,GACd,EACA,EACA,EAAwB,CAAC,EACD,CACxB,IAAM,EAAK,EAAc,EAAO,CAAO,EAEvC,OAAO,EAAQ,EAAO,CAAE,OAAQ,EAAQ,OAAQ,IAAG,CAAC,CAAC,CAAC,IAAI,CAAQ,CACpE,CAmBA,SAAgB,EAAW,EAAkB,EAAgB,EAA6B,CAAC,EAAsB,CAC/G,GAAM,CAAE,cAAa,SAAQ,oBAAmB,eAAc,gBAAiB,EACzE,EAAkB,CAAE,cAAa,oBAAmB,eAAc,cAAa,EAMrF,GAAI,EAHD,IAAgB,IAAA,IAAa,EAAe,IAAI,CAA2B,GAC3E,IAAiB,IAAA,IAAa,EAAe,IAAI,CAA4B,IAE1D,aAAiB,EAAA,SAAS,SAAW,aAAe,EAAA,SAAS,QACjF,OAAO,EAAI,MAAM,EAAO,CAAgE,EAG1F,IAAM,EAAK,EAAoB,CAAC,EAAO,CAAG,EAAG,CAAO,EAEpD,OAAO,EAAQ,EAAK,CAAE,SAAQ,IAAG,CAAC,CAAC,CAAC,MAAM,EAAQ,EAAO,CAAE,SAAQ,IAAG,CAAC,EAAG,CAAe,CAC3F,CAWA,SAAgB,EAAQ,EAAoC,CAC1D,OACE,aAAiB,EAAA,SAAS,SAC1B,aAAiB,EAAA,SAAS,eAC1B,aAAiB,EAAA,SAAS,eAC1B,aAAiB,EAAA,SAAS,SAE9B,CAuBA,SAAgB,EAAM,EAAe,EAAyB,CAC5D,GAAI,IAAO,QAAS,OAAO,EAAW,CAAK,EAE3C,GAAI,IAAO,UAAW,OAAO,EAAa,CAAK,EAE/C,GAAI,IAAO,iBAAkB,OAAO,EAAmB,CAAK,EAE5D,GAAI,IAAO,aAAc,OAAO,EAAe,CAAK,EAEpD,GAAI,CACF,OAAO,EAAA,SAAS,cAAc,KAAK,CAAK,CAC1C,MAAQ,CAER,CAEA,GAAI,CACF,OAAO,EAAA,SAAS,QAAQ,KAAK,CAAK,CACpC,MAAQ,CAER,CAKA,GAAI,EAAM,SAAS,GAAG,EACpB,GAAI,CACF,OAAO,EAAA,SAAS,cAAc,KAAK,CAAK,CAC1C,MAAQ,CAER,MAEA,GAAI,CACF,OAAO,EAAA,SAAS,UAAU,KAAK,CAAK,CACtC,MAAQ,CAER,CAGF,EACE,sCAAsC,EAAM,0EAC9C,CACF,CC5PA,IAAM,EAAwC,CAC5C,KAAM,EACN,YAAa,EACb,YAAa,EACb,OAAQ,EACR,WAAY,EACZ,OAAQ,CACV,EAEM,EAAoF,CACxF,IAAK,EACL,KAAM,CAAE,YAAa,EAAG,YAAa,EAAG,OAAQ,EAAG,WAAY,EAAG,OAAQ,CAAE,EAC5E,OAAQ,CAAE,YAAa,EAAG,YAAa,EAAG,WAAY,EAAG,OAAQ,CAAE,EACnE,MAAO,CAAE,GAAG,EAAW,IAAK,CAAE,EAC9B,KAAM,CAAE,GAAG,EAAW,IAAK,EAAG,MAAO,CAAE,CACzC,EAMA,SAAgB,EACd,EACA,EACA,EACkB,CAClB,IAAM,EAAQ,EAAQ,EAAO,CAAE,OAAQ,EAAQ,OAAQ,GAAI,EAAQ,EAAG,CAAC,EAEvE,GAAI,IAAS,OAAQ,CACnB,IAAM,GAAkB,EAAM,WAAa,EAAQ,cAAgB,GAAK,GAAK,EAE7E,OAAO,EAAM,SAAS,CAAE,KAAM,CAAe,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC,CAAC,UAAU,CAC5E,CAEA,OAAO,EAAM,KAAK,EAAe,EAAK,CAAC,CAAC,UAAU,CACpD,CClCA,IAAM,EAA6D,CACjE,IAAK,CAAE,KAAM,CAAE,EACf,KAAM,CAAE,MAAO,CAAE,EACjB,OAAQ,CAAE,QAAS,CAAE,EACrB,MAAO,CAAE,OAAQ,CAAE,EACnB,KAAM,CAAE,MAAO,CAAE,EACjB,KAAM,CAAE,MAAO,CAAE,CACnB,EAgBA,SAAgB,GAAQ,EAAkB,EAAoB,EAA2B,CAAC,EAA2B,CACnH,IAAM,EAAK,EAAc,EAAO,CAAO,EAEvC,OAAO,EAAY,EAAO,EAAM,CAAE,KAAI,aAAc,EAAQ,YAAa,CAAC,CAAC,CAAC,mBAAmB,CAAE,CACnG,CAWA,SAAgB,GAAM,EAAkB,EAAoB,EAA2B,CAAC,EAA2B,CACjH,IAAM,EAAK,EAAc,EAAO,CAAO,EAGvC,OAFqB,EAAY,EAAO,EAAM,CAAE,KAAI,aAAc,EAAQ,YAAa,CAEhF,CAAA,CAAa,mBAAmB,CAAE,CAAC,CAAC,IAAI,EAAc,EAAK,CAAC,CAAC,SAAS,CAAE,YAAa,CAAE,CAAC,CACjG,CC1CA,SAAS,GACP,EACA,EACA,EACA,EACqD,CAErD,IAAM,EAAW,CAAE,GADR,EAAoB,CAAC,EAAG,CAAC,EAAG,CACpB,EAAI,aAAc,EAAQ,YAAa,EAE1D,MAAO,CACL,KAAM,EAAY,EAAG,EAAM,CAAQ,EACnC,MAAO,EAAY,EAAG,EAAM,CAAQ,CACtC,CACF,CAEA,SAAS,EACP,EACA,EACA,EACA,EACA,EACgF,CAEhF,IAAM,EAAW,CAAE,GADR,EAAoB,CAAC,EAAO,EAAO,CAAG,EAAG,CACjC,EAAI,aAAc,EAAQ,YAAa,EACpD,EAAS,EAAY,EAAO,EAAM,CAAQ,EAC1C,CAAC,EAAO,GAAS,EAAe,EAAY,EAAO,EAAM,CAAQ,EAAG,EAAY,EAAK,EAAM,CAAQ,CAAC,EAE1G,MAAO,CAAE,QAAO,SAAQ,OAAM,CAChC,CAEA,SAAS,EAAc,EAAc,EAAc,EAAiC,CAClF,GAAI,CAAC,EAAQ,KACX,OAAO,EAAA,SAAS,QAAQ,QAAQ,EAAU,EAAG,CAAO,EAAG,EAAU,EAAG,CAAO,CAAC,EAG9E,GAAM,CAAE,OAAM,SAAU,GAAmB,EAAG,EAAG,EAAQ,KAAM,CAAO,EAEtE,OAAO,EAAA,SAAS,QAAQ,QAAQ,EAAM,CAAK,CAC7C,CAcA,SAAgB,GAAS,EAAc,EAAc,EAA0B,CAAC,EAAY,CAC1F,OAAO,EAAc,EAAG,EAAG,CAAO,EAAI,CACxC,CAYA,SAAgB,GAAQ,EAAc,EAAc,EAA0B,CAAC,EAAY,CACzF,OAAO,EAAc,EAAG,EAAG,CAAO,EAAI,CACxC,CAWA,SAAgB,GAAO,EAAc,EAAc,EAA0B,CAAC,EAAY,CACxF,OAAO,EAAc,EAAG,EAAG,CAAO,IAAM,CAC1C,CAeA,SAAgB,GAAO,EAAkB,EAAkB,EAAgB,EAA0B,CAAC,EAAY,CAChH,GAAI,CAAC,EAAQ,KAAM,CACjB,IAAM,EAAS,EAAU,EAAO,CAAO,EACjC,CAAC,EAAO,GAAS,EAAe,EAAU,EAAO,CAAO,EAAG,EAAU,EAAK,CAAO,CAAC,EAExF,OAAO,EAAA,SAAS,QAAQ,QAAQ,EAAO,CAAM,GAAK,GAAK,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,GAAK,CACpG,CAEA,GAAM,CAAE,QAAO,SAAQ,SAAU,EAAqB,EAAO,EAAO,EAAK,EAAQ,KAAM,CAAO,EAE9F,OAAO,EAAA,SAAS,QAAQ,QAAQ,EAAO,CAAM,GAAK,GAAK,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,GAAK,CACpG,CAiCA,SAAgB,EACd,EACA,EACA,EACA,EAA0B,CAAC,EACgB,CAC3C,IAAM,EAAU,aAAiB,EAAA,SAAS,cACpC,EAAK,EAAU,EAAM,WAAa,IAAA,GAExC,GAAI,CAAC,EAAQ,KAAM,CACjB,IAAM,EAAS,EAAU,EAAO,CAAO,EACjC,CAAC,EAAO,GAAS,EAAe,EAAU,EAAO,CAAO,EAAG,EAAU,EAAK,CAAO,CAAC,EAEpF,EAMJ,MAJA,CAEK,EAFD,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,EAAI,EAAa,EAClD,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,EAAI,EAAa,EACjD,EAER,GAAW,EAAK,EAAQ,mBAAmB,CAAE,EAAI,CAC1D,CAEA,GAAM,CAAE,QAAO,SAAQ,SAAU,EAAqB,EAAO,EAAO,EAAK,EAAQ,KAAM,CAAO,EAE1F,EAEJ,AAEK,EAFD,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,EAAI,EAAa,EAClD,EAAA,SAAS,QAAQ,QAAQ,EAAQ,CAAK,EAAI,EAAa,EACjD,EAGf,IAAM,EAAQ,EAAQ,IAAM,GAAM,EAAoB,CAAC,EAAO,EAAO,CAAG,EAAG,CAAO,EAElF,OAAO,EAAU,EAAQ,mBAAmB,CAAK,EAAI,CACvD,CC7JA,IAAM,EAAsB,IAE5B,SAAS,EAAqB,EAAuB,EAAa,EAAqB,CACrF,IAAM,EAAS,EAAM,IAAI,CAAG,EAE5B,GAAI,IAAW,IAAA,GAAW,OAAO,EAEjC,GAAI,EAAM,MAAQ,EAAqB,CACrC,IAAM,EAAS,EAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAE/B,IAAW,IAAA,IAAW,EAAM,OAAO,CAAM,CAC/C,CAEA,IAAM,EAAQ,EAAQ,EAItB,OAFA,EAAM,IAAI,EAAK,CAAK,EAEb,CACT,CAEA,IAAM,EAA4B,IAAI,IAChC,EAAgC,IAAI,IACpC,EAA2B,IAAI,IAI/B,EAAoE,CACxE,YAAa,CAAE,UAAW,OAAQ,EAClC,KAAM,CAAE,UAAW,OAAQ,UAAW,MAAO,EAC7C,OAAQ,CAAE,UAAW,SAAU,UAAW,OAAQ,EAClD,MAAO,CAAE,UAAW,QAAS,UAAW,OAAQ,EAChD,YAAa,CAAE,UAAW,OAAQ,CACpC,EAIA,SAAS,EAAqB,EAA6C,CACzE,OAAO,KAAK,UACV,OAAO,QAAQ,CAAO,CAAC,CACpB,QAAQ,EAAG,KAAW,IAAU,IAAA,EAAS,CAAC,CAC1C,MAAM,CAAC,GAAI,CAAC,KAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CACtC,KAAK,CAAC,EAAK,KAAW,CAAC,EAAK,OAAO,CAAK,CAAC,CAAC,CAC/C,CACF,CAEA,SAAS,EAAc,EAAwB,EAA0C,CACvF,IAAM,EAAK,EAAQ,IAAM,EACnB,EAAS,EAAQ,OAEvB,GAAI,EAAQ,OAAS,IAAA,GAGnB,OAAO,EAAkB,EAA2B,GAFhC,OAAO,GAAU,EAAE,EAAE,QAAQ,GAAM,GAAG,GAAG,EAAqB,EAAQ,IAAI,QAE1B,CAClE,IAAM,EAAc,IAAO,IAAA,GAAgD,EAAQ,KAA5C,CAAE,GAAG,EAAQ,KAAM,SAAU,CAAG,EAEvE,OAAO,IAAI,KAAK,eAAe,EAAQ,CAAW,CACpD,CAAC,EAGH,IAAM,EAAU,EAAQ,SAAW,SAGnC,OAAO,EACL,EACA,GAJkB,OAAO,GAAU,EAAE,EAAE,GAAG,EAAQ,GAAG,GAAM,SAKrD,IAAI,KAAK,eAAe,EAAQ,CAAE,GAAG,EAAe,GAAU,SAAU,CAAG,CAAC,CACpF,CACF,CAEA,SAAS,EAAqB,EAIF,CAG1B,OAAO,EACL,EACA,GAJkB,OAAO,EAAQ,QAAU,EAAE,EAAE,GAAG,EAAQ,SAAW,OAAO,GAAG,EAAQ,OAAS,aAM9F,IAAI,KAAK,mBAAmB,EAAQ,OAAQ,CAC1C,QAAS,EAAQ,SAAW,OAC5B,MAAO,EAAQ,OAAS,MAC1B,CAAC,CACL,CACF,CAEA,SAAS,EAAqB,EAGD,CAC3B,IAAM,EAAyB,KAM/B,OAJK,EAAuB,eAIrB,EACL,EACA,GAJkB,OAAO,EAAQ,QAAU,EAAE,EAAE,GAAG,EAAQ,OAAS,SAK7D,IAAI,EAAuB,eAAgB,EAAQ,OAAQ,CAAE,MAAO,EAAQ,KAAM,CAAC,CAC3F,EARmD,IASrD,CAIA,IAAM,EAAqB,GACrB,EAAmB,KACnB,EAAkB,MAClB,EAAmB,OACnB,EAAoB,QACpB,EAAmB,SAInB,GACJ,CACE,CAAE,MAAO,EAAG,mBAAoB,EAAoB,KAAM,QAAS,EACnE,CAAE,MAAO,EAAoB,mBAAoB,EAAmB,EAAoB,KAAM,QAAS,EACvG,CAAE,MAAO,EAAkB,mBAAoB,EAAkB,EAAkB,KAAM,MAAO,EAChG,CAAE,MAAO,EAAiB,mBAAoB,EAAmB,EAAiB,KAAM,KAAM,EAC9F,CAAE,MAAO,EAAkB,mBAAoB,EAAoB,EAAkB,KAAM,MAAO,EAClG,CAAE,MAAO,EAAmB,mBAAoB,GAAI,KAAM,OAAQ,EAClE,CAAE,MAAO,EAAkB,mBAAoB,IAA0B,KAAM,MAAO,CACxF,EAEF,SAAS,GAAe,EAAuE,CACxF,OAAO,SAAS,CAAO,GAAG,EAAK,uDAAuD,EAE3F,IAAM,EAAiB,KAAK,MAAM,CAAO,EAEzC,IAAK,GAAM,CAAE,QAAO,qBAAoB,UAAU,GAAgB,CAChE,IAAM,EAAQ,KAAK,MAAM,EAAiB,CAAK,EAE/C,GAAI,KAAK,IAAI,CAAK,EAAI,EAAoB,MAAO,CAAE,OAAM,OAAM,CACjE,CAEA,MAAO,CAAE,KAAM,OAAQ,MAAO,KAAK,MAAM,EAAiB,CAAgB,CAAE,CAC9E,CAKA,IAAM,GAAiB,CACrB,QACA,SACA,QACA,OACA,QACA,UACA,UACA,eACA,eACA,aACF,EAGA,SAAS,GAAsB,EAAqC,CAClE,IAAM,EAAkB,CAAC,EAEzB,IAAK,IAAM,KAAQ,GAAgB,CACjC,IAAM,EAAQ,KAAK,IAAI,EAAS,EAAe,EAE3C,IAAU,GAAG,EAAM,KAAK,GAAG,EAAM,GAAG,IAAU,EAAI,EAAK,MAAM,EAAG,EAAE,EAAI,GAAM,CAClF,CAEA,OAAO,EAAM,SAAW,EAAI,YAAc,EAAM,KAAK,IAAI,CAC3D,CAQA,SAAS,EAAe,EAAkB,EAAgB,EAAwB,EAAoC,CACpH,GAAI,EAAQ,GAAI,OAAO,EAAQ,GAE/B,IAAM,EAAU,aAAiB,EAAA,SAAS,cAAgB,EAAM,WAAa,IAAA,GACvE,EAAQ,aAAe,EAAA,SAAS,cAAgB,EAAI,WAAa,IAAA,GAMvE,OAJI,GAAW,GAAS,IAAY,GAClC,EAAK,GAAG,EAAO,sFAAsF,EAGhG,GAAW,CACpB,CAgBA,SAAgB,GAAO,EAAkB,EAAyB,CAAC,EAAW,CAC5E,IAAM,EAAK,EAAQ,KAAO,aAAiB,EAAA,SAAS,cAAgB,EAAM,WAAa,IAAA,IAEvF,OAAO,EAAc,EAAS,CAAE,CAAC,CAAC,OAAO,IAAI,KAAK,EAAU,EAAO,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAC/F,CAWA,SAAgB,GAAY,EAAkB,EAAgB,EAAyB,CAAC,EAAW,CACjG,IAAM,EAAK,EAAe,EAAO,EAAK,EAAS,aAAa,EAG5D,OAFkB,EAAc,EAAS,CAElC,CAAA,CAAU,YACf,IAAI,KAAK,EAAU,EAAO,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,EACnD,IAAI,KAAK,EAAU,EAAK,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,CACnD,CACF,CAYA,SAAgB,GACd,EACA,EACA,EAAyB,CAAC,EAC6B,CACvD,IAAM,EAAK,EAAe,EAAO,EAAK,EAAS,kBAAkB,EAGjE,OAFkB,EAAc,EAAS,CAElC,CAAA,CAAU,mBACf,IAAI,KAAK,EAAU,EAAO,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,EACnD,IAAI,KAAK,EAAU,EAAK,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,CACnD,CACF,CAYA,SAAgB,GAAc,EAAkB,EAAuB,CAAC,EAAW,CACjF,OAAO,EAAU,EAAO,CAAO,CAAC,CAAC,SAAS,CAC5C,CAmBA,SAAgB,GAAY,EAAkB,EAAuB,CAAC,EAAW,CAG/E,OAAO,EAAQ,EAAO,CAAE,GAFb,EAAc,EAAO,CAER,CAAG,CAAC,CAAC,CAAC,SAAS,CACzC,CAeA,SAAgB,GAAe,EAA0B,EAAiC,CAAC,EAAW,CACpG,IAAM,EAAS,aAAiB,EAAA,SAAS,QAAU,EAAQ,EAAM,UAAU,EACrE,EAAO,EAAQ,KACjB,EAAQ,gBAAgB,EAAA,SAAS,QAC/B,EAAQ,KACR,EAAQ,KAAK,UAAU,EACzB,EAAA,SAAS,IAAI,QAAQ,EAEnB,CAAE,OAAM,SAAU,IADK,EAAO,kBAAoB,EAAK,mBAAqB,GACxB,EAE1D,OAAO,EAAqB,CAAO,CAAC,CAAC,OAAO,EAAO,CAAI,CACzD,CAWA,SAAgB,EAAc,EAA0D,CACtF,GAAI,CACF,OAAO,EAAA,SAAS,SAAS,KAAK,CAAK,CACrC,MAAQ,CACN,EAAK,4BAA4B,OAAO,CAAK,EAAE,kEAAkE,CACnH,CACF,CAYA,SAAgB,GAAe,EAAuC,EAAiC,CAAC,EAAW,CACjH,IAAM,EAAW,EAAc,CAAK,EAC9B,EAAY,EAAqB,CAAO,EAI9C,OAFI,EAAkB,EAAU,OAAO,CAAQ,EAExC,GAAsB,CAAQ,CACvC,CAaA,SAAgB,GAAY,EAAkB,EAAyB,CAAC,EAA8B,CACpG,IAAM,EAAK,EAAQ,KAAO,aAAiB,EAAA,SAAS,cAAgB,EAAM,WAAa,IAAA,IAEvF,OAAO,EAAc,EAAS,CAAE,CAAC,CAAC,cAAc,IAAI,KAAK,EAAU,EAAO,CAAE,IAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CACtG,CAkBA,SAAgB,GAAS,EAAsB,EAA6C,CAAC,EAAW,CACtG,GAAM,CAAE,OAAM,SAAU,EAGxB,MAAO,GAFW,EAAQ,OAAS,IAAI,KAAK,aAAa,EAAQ,MAAM,CAAC,CAAC,OAAO,CAAK,EAAI,OAAO,CAAK,EAEjF,GAAG,IAAU,EAAI,EAAO,GAAG,EAAK,IACtD,CCpZA,IAAM,EAAuB,IAAI,QAEjC,SAAS,GAAsC,EAAkE,CAC/G,IAAM,EAAS,EAAqB,IAAI,CAAU,EAElD,GAAI,EAAQ,OAAO,EAEnB,IAAM,EAAU,OAAO,KAAK,CAAU,CAAC,CACpC,IAAK,IAAS,CAAE,MAAK,GAAI,GAAa,EAAW,EAAI,CAAE,EAAE,CAAC,CAC1D,MAAM,EAAG,IAAM,EAAE,GAAK,EAAE,EAAE,EAI7B,OAFA,EAAqB,IAAI,EAAY,CAAM,EAEpC,CACT,CA4BA,SAAgB,EACd,EACA,EACA,EAAuB,CAAC,EACxB,EAAM,EAAA,SAAS,IAAI,QAAQ,EACjB,CAKV,IAAM,EAJS,EAAU,EAAM,CAAO,CAAC,CAAC,kBAC1B,EAAI,kBAKlB,IAAK,GAAM,CAAE,MAAK,QAAQ,GAAoB,CAAU,EACtD,GAAI,GAAU,EAAI,OAAO,EAG3B,OAAO,IACT,CAGA,SAAS,GAAa,EAAyC,CAC7D,IAAM,EAAI,EAAA,SAAS,SAAS,KAAK,CAAQ,EAGzC,OACG,EAAE,OAAS,GAAK,GAAK,GACrB,EAAE,QAAU,GAAK,GACjB,EAAE,OAAS,GAAK,EAAI,OACpB,EAAE,MAAQ,GAAK,OACf,EAAE,OAAS,GAAK,MAChB,EAAE,SAAW,GAAK,KAClB,EAAE,SAAW,GAAK,KAClB,EAAE,cAAgB,IAClB,EAAE,cAAgB,GAAK,KACvB,EAAE,aAAe,GAAK,GAE3B,CAIA,IAAM,GAAoF,CACxF,CAAE,MAAO,QAAS,KAAM,MAAO,EAC/B,CAAE,MAAO,SAAU,KAAM,OAAQ,EACjC,CAAE,MAAO,QAAS,KAAM,MAAO,EAC/B,CAAE,MAAO,OAAQ,KAAM,KAAM,EAC7B,CAAE,MAAO,QAAS,KAAM,MAAO,EAC/B,CAAE,MAAO,UAAW,KAAM,QAAS,EACnC,CAAE,MAAO,UAAW,KAAM,QAAS,EACnC,CAAE,MAAO,eAAgB,KAAM,aAAc,CAC/C,EAEA,SAAS,EAAW,EAA2B,EAA8C,CAC3F,OAAO,EAAA,SAAS,cAAc,QAAQ,EAAG,CAAC,GAAK,EAC3C,EAAE,MAAM,EAAG,CAAE,YAAa,MAAO,CAAC,EAClC,EAAE,MAAM,EAAG,CAAE,YAAa,MAAO,CAAC,CACxC,CAEA,SAAS,EAAgB,EAA6C,CACpE,IAAK,GAAM,CAAE,QAAO,UAAU,GAAY,CACxC,IAAM,EAAQ,KAAK,IAAI,EAAS,EAAgB,EAEhD,GAAI,EAAQ,EAAG,MAAO,CAAE,OAAM,OAAM,CACtC,CAEA,MAAO,CAAE,KAAM,cAAe,MAAO,CAAE,CACzC,CAmBA,SAAgB,GAAS,EAAc,EAAe,EAAuB,CAAC,EAAmB,CAC/F,IAAM,EAAiB,GAAK,EAAA,SAAS,IAAI,QAAQ,EAIjD,GAAI,CAAC,EAAQ,IAAM,aAAa,EAAA,SAAS,SAAW,aAAe,EAAA,SAAS,QAC1E,OAAO,EAAgB,EAAW,EAAE,mBAAmB,KAAK,EAAG,EAAI,mBAAmB,KAAK,CAAC,CAAC,EAI/F,IAAM,EAAK,EAAoB,CAAC,EAAG,CAAG,EAAG,CAAO,EAEhD,OAAO,EAAgB,EAAW,EAAQ,EAAG,CAAE,IAAG,CAAC,EAAG,EAAQ,EAAK,CAAE,IAAG,CAAC,CAAC,CAAC,CAC7E,CCjHA,SAAgB,GACd,EACA,EACA,EACA,EAAuB,CAAC,EACW,CACnC,IAAM,EAAK,EAAc,EAAO,CAAO,EACjC,EAAa,EAAQ,EAAO,CAAE,GAAG,EAAS,IAAG,CAAC,EAC9C,EAAW,EAAQ,EAAK,CAAE,GAAG,EAAS,IAAG,CAAC,EAGhD,GAAI,EAAA,SAAS,cAAc,QAAQ,EAAW,IAAI,CAAI,EAAG,CAAU,GAAK,EACtE,MAAM,IAAI,EAAuB,+CAA+C,EAGlF,OAAO,GAAmB,EAAY,EAAU,CAAI,CACtD,CAEA,SAAU,GACR,EACA,EACA,EACmC,CACnC,IAAI,EAAU,EAEd,KAAO,EAAA,SAAS,cAAc,QAAQ,EAAS,CAAG,GAAK,GACrD,MAAM,EACN,EAAU,EAAQ,IAAI,CAAI,CAE9B,CA2BA,SAAgB,GACd,EACA,EACA,EAAuB,CAAC,EACW,CACnC,GAAM,CAAE,QAAO,YAAW,WAAW,EAAG,SAAU,EAE5C,EAAK,EAAc,EAAO,CAAO,EAEjC,EACJ,IAAc,QACV,CAAE,KAAM,CAAS,EACjB,IAAc,SACZ,CAAE,MAAO,CAAS,EAClB,IAAc,UACZ,CAAE,OAAQ,CAAS,EACnB,CAAE,MAAO,CAAS,EAEtB,EAAa,IAAU,IAAA,GAAmD,IAAA,GAAvC,EAAU,EAAO,CAAE,GAAG,EAAS,IAAG,CAAC,EAE5E,OAAO,GAAoB,EAAQ,EAAO,CAAE,GAAG,EAAS,IAAG,CAAC,EAAG,EAAM,EAAO,CAAU,CACxF,CAEA,SAAU,GACR,EACA,EACA,EACA,EACmC,CACnC,IAAI,EAAU,EACV,EAAU,EAEd,KAGM,EAFA,IAAU,IAAA,IAAa,GAAW,GAElC,IAAe,IAAA,IAAa,EAAA,SAAS,QAAQ,QAAQ,EAAQ,UAAU,EAAG,CAAU,EAAI,IAE5F,MAAM,EACN,IACA,EAAU,EAAQ,IAAI,CAAI,CAE9B"}
package/dist/tempo.js ADDED
@@ -0,0 +1,2 @@
1
+ import{Temporal as e,Temporal as t}from"@js-temporal/polyfill";var n=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},r=class extends n{},i=class extends n{},a=class extends n{},o=class extends n{};function s(e,t=r){throw new t(e)}function c(e){try{t.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(e)}catch{s(`Unknown or invalid timezone: "${e}". Expected an IANA timezone name (e.g. "America/New_York") or UTC offset (e.g. "+05:30").`,i)}return e}function l(e,n){let r=n.tz??(e instanceof t.ZonedDateTime?e.timeZoneId:void 0);return r||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),c(r)}function u(e,n){if(n.tz)return c(n.tz);let r;for(let n of e){if(!(n instanceof t.ZonedDateTime))continue;let e=n.timeZoneId;if(!r){r=e;continue}r!==e&&s(`Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`)}return r||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),r}function d(e,n){return t.Instant.compare(e,n)<=0?[e,n]:[n,e]}var f=new Set([`day`,`month`,`week`,`year`]),p=30.4375*864e5;function m(e,n={}){if(e instanceof t.Instant)return e;if(e instanceof t.ZonedDateTime)return e.toInstant();if(e instanceof t.PlainDateTime)return n.tz||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),e.toZonedDateTime(c(n.tz),{disambiguation:n.prefer}).toInstant();if(e instanceof t.PlainDate)return n.tz||s(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,a),e.toZonedDateTime({timeZone:c(n.tz)}).toInstant();s(`Unsupported time input type: ${String(e)}`,o)}function h(e,n){let r=n,i=c(r.tz);if(e instanceof t.ZonedDateTime)return e.withTimeZone(i);if(e instanceof t.PlainDateTime)return e.toZonedDateTime(i,{disambiguation:r.prefer});if(e instanceof t.PlainDate)return e.toZonedDateTime({timeZone:i});if(e instanceof t.Instant)return e.toZonedDateTimeISO(i);s(`Unsupported time input type: ${String(e)}`,o)}function ee(e,t){return h(e,{tz:t})}function g(e){return t.Now.zonedDateTimeISO(e)}function _(){return t.Now.instant()}function v(e){try{return t.ZonedDateTime.from(e)}catch{s(`Invalid zoned date-time string: "${e}". Expected an ISO 8601 string with offset and timezone (e.g. 2026-03-21T10:00:00+01:00[Europe/Berlin]).`)}}function y(e){try{return t.PlainDate.from(e)}catch{s(`Invalid plain date string: "${e}". Expected an ISO 8601 date string (e.g. YYYY-MM-DD).`)}}function b(e){try{return t.PlainDateTime.from(e)}catch{s(`Invalid date/time string: "${e}". Expected an ISO 8601 date or date-time string (e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).`)}}function x(e){try{return t.Instant.from(e)}catch{s(`Invalid instant string: "${e}". Expected an ISO 8601 UTC string (e.g. YYYY-MM-DDTHH:mm:ssZ).`)}}function te(e,t,n={}){let r=l(e,n);return h(e,{prefer:n.prefer,tz:r}).add(t)}function S(e,n,r={}){let{largestUnit:i,prefer:a,roundingIncrement:o,roundingMode:s,smallestUnit:c}=r,l={largestUnit:i,roundingIncrement:o,roundingMode:s,smallestUnit:c};if(!(i!==void 0&&f.has(i)||c!==void 0&&f.has(c))&&e instanceof t.Instant&&n instanceof t.Instant)return n.since(e,l);let d=u([e,n],r);return h(n,{prefer:a,tz:d}).since(h(e,{prefer:a,tz:d}),l)}function C(e){return e instanceof t.Instant||e instanceof t.ZonedDateTime||e instanceof t.PlainDateTime||e instanceof t.PlainDate}function w(e,n){if(n===`zoned`)return v(e);if(n===`instant`)return x(e);if(n===`plain-datetime`)return b(e);if(n===`plain-date`)return y(e);try{return t.ZonedDateTime.from(e)}catch{}try{return t.Instant.from(e)}catch{}if(e.includes(`T`))try{return t.PlainDateTime.from(e)}catch{}else try{return t.PlainDate.from(e)}catch{}s(`Unable to parse date/time string: "${e}". Expected ISO 8601 ZonedDateTime, Instant, PlainDateTime, or PlainDate.`)}var T={hour:0,microsecond:0,millisecond:0,minute:0,nanosecond:0,second:0},E={day:T,hour:{microsecond:0,millisecond:0,minute:0,nanosecond:0,second:0},minute:{microsecond:0,millisecond:0,nanosecond:0,second:0},month:{...T,day:1},year:{...T,day:1,month:1}};function D(e,t,n){let r=h(e,{prefer:n.prefer,tz:n.tz});if(t===`week`){let e=(r.dayOfWeek-(n.weekStartsOn??1)+7)%7;return r.subtract({days:e}).with(T).toInstant()}return r.with(E[t]).toInstant()}var O={day:{days:1},hour:{hours:1},minute:{minutes:1},month:{months:1},week:{weeks:1},year:{years:1}};function ne(e,t,n={}){let r=l(e,n);return D(e,t,{tz:r,weekStartsOn:n.weekStartsOn}).toZonedDateTimeISO(r)}function re(e,t,n={}){let r=l(e,n);return D(e,t,{tz:r,weekStartsOn:n.weekStartsOn}).toZonedDateTimeISO(r).add(O[t]).subtract({nanoseconds:1})}function ie(e,t,n,r){let i={tz:u([e,t],r),weekStartsOn:r.weekStartsOn};return{left:D(e,n,i),right:D(t,n,i)}}function k(e,t,n,r,i){let a={tz:u([e,t,n],i),weekStartsOn:i.weekStartsOn},o=D(e,r,a),[s,c]=d(D(t,r,a),D(n,r,a));return{lower:s,target:o,upper:c}}function A(e,n,r){if(!r.unit)return t.Instant.compare(m(e,r),m(n,r));let{left:i,right:a}=ie(e,n,r.unit,r);return t.Instant.compare(i,a)}function ae(e,t,n={}){return A(e,t,n)<0}function oe(e,t,n={}){return A(e,t,n)>0}function se(e,t,n={}){return A(e,t,n)===0}function ce(e,n,r,i={}){if(!i.unit){let a=m(e,i),[o,s]=d(m(n,i),m(r,i));return t.Instant.compare(o,a)<=0&&t.Instant.compare(a,s)<=0}let{lower:a,target:o,upper:s}=k(e,n,r,i.unit,i);return t.Instant.compare(a,o)<=0&&t.Instant.compare(o,s)<=0}function j(e,n,r,i={}){let a=e instanceof t.ZonedDateTime,o=a?e.timeZoneId:void 0;if(!i.unit){let s=m(e,i),[c,l]=d(m(n,i),m(r,i)),u;return u=t.Instant.compare(s,c)<0?c:t.Instant.compare(s,l)>0?l:s,a&&o?u.toZonedDateTimeISO(o):u}let{lower:s,target:c,upper:l}=k(e,n,r,i.unit,i),f;f=t.Instant.compare(c,s)<0?s:t.Instant.compare(c,l)>0?l:c;let p=i.tz??o??u([e,n,r],i);return a?f.toZonedDateTimeISO(p):f}var M=128;function N(e,t,n){let r=e.get(t);if(r!==void 0)return r;if(e.size>=M){let t=e.keys().next().value;t!==void 0&&e.delete(t)}let i=n();return e.set(t,i),i}var P=new Map,F=new Map,I=new Map,L={"date-only":{dateStyle:`short`},long:{dateStyle:`full`,timeStyle:`long`},medium:{dateStyle:`medium`,timeStyle:`short`},short:{dateStyle:`short`,timeStyle:`short`},"time-only":{timeStyle:`short`}};function R(e){return JSON.stringify(Object.entries(e).filter(([,e])=>e!==void 0).sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>[e,String(t)]))}function z(e,t){let n=e.tz??t,r=e.locale;if(e.intl!==void 0)return N(P,`${String(r??``)}|intl|${n??``}|${R(e.intl)}`,()=>{let t=n===void 0?e.intl:{...e.intl,timeZone:n};return new Intl.DateTimeFormat(r,t)});let i=e.pattern??`medium`;return N(P,`${String(r??``)}|${i}|${n??``}`,()=>new Intl.DateTimeFormat(r,{...L[i],timeZone:n}))}function B(e){return N(F,`${String(e.locale??``)}|${e.numeric??`auto`}|${e.style??`long`}`,()=>new Intl.RelativeTimeFormat(e.locale,{numeric:e.numeric??`auto`,style:e.style??`long`}))}function V(e){let t=Intl;return t.DurationFormat?N(I,`${String(e.locale??``)}|${e.style??``}`,()=>new t.DurationFormat(e.locale,{style:e.style})):null}var H=60,U=3600,W=86400,G=604800,K=2629800,q=31557600,le=[{scale:1,thresholdToPromote:H,unit:`second`},{scale:H,thresholdToPromote:U/H,unit:`minute`},{scale:U,thresholdToPromote:W/U,unit:`hour`},{scale:W,thresholdToPromote:G/W,unit:`day`},{scale:G,thresholdToPromote:K/G,unit:`week`},{scale:K,thresholdToPromote:12,unit:`month`},{scale:q,thresholdToPromote:1/0,unit:`year`}];function ue(e){Number.isFinite(e)||s(`formatRelative received a non-finite time difference.`);let t=Math.round(e);for(let{scale:e,thresholdToPromote:n,unit:r}of le){let i=Math.round(t/e);if(Math.abs(i)<n)return{unit:r,value:i}}return{unit:`year`,value:Math.round(t/q)}}var de=[`years`,`months`,`weeks`,`days`,`hours`,`minutes`,`seconds`,`milliseconds`,`microseconds`,`nanoseconds`];function fe(e){let t=[];for(let n of de){let r=Math.abs(e[n]);r!==0&&t.push(`${r} ${r===1?n.slice(0,-1):n}`)}return t.length===0?`0 seconds`:t.join(`, `)}function J(e,n,r,i){if(r.tz)return r.tz;let a=e instanceof t.ZonedDateTime?e.timeZoneId:void 0,o=n instanceof t.ZonedDateTime?n.timeZoneId:void 0;return a&&o&&a!==o&&s(`${i} received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`),a??o}function pe(e,n={}){let r=n.tz??(e instanceof t.ZonedDateTime?e.timeZoneId:void 0);return z(n,r).format(new Date(m(e,{tz:r}).epochMilliseconds))}function me(e,t,n={}){let r=J(e,t,n,`formatRange`);return z(n,r).formatRange(new Date(m(e,{tz:r}).epochMilliseconds),new Date(m(t,{tz:r}).epochMilliseconds))}function he(e,t,n={}){let r=J(e,t,n,`formatRangeParts`);return z(n,r).formatRangeToParts(new Date(m(e,{tz:r}).epochMilliseconds),new Date(m(t,{tz:r}).epochMilliseconds))}function ge(e,t={}){return m(e,t).toString()}function _e(e,t={}){return h(e,{tz:l(e,t)}).toString()}function ve(e,n={}){let r=e instanceof t.Instant?e:e.toInstant(),i=n.base?n.base instanceof t.Instant?n.base:n.base.toInstant():t.Now.instant(),{unit:a,value:o}=ue((r.epochMilliseconds-i.epochMilliseconds)/1e3);return B(n).format(o,a)}function Y(e){try{return t.Duration.from(e)}catch{s(`Invalid duration input: "${String(e)}". Expected an ISO 8601 duration string or Temporal.DurationLike.`)}}function ye(e,t={}){let n=Y(e),r=V(t);return r?r.format(n):fe(n)}function be(e,n={}){let r=n.tz??(e instanceof t.ZonedDateTime?e.timeZoneId:void 0);return z(n,r).formatToParts(new Date(m(e,{tz:r}).epochMilliseconds))}function xe(e,t={}){let{unit:n,value:r}=e;return`${t.locale?new Intl.NumberFormat(t.locale).format(r):String(r)} ${r===1?n:`${n}s`}`}var X=new WeakMap;function Se(e){let t=X.get(e);if(t)return t;let n=Object.keys(e).map(t=>({key:t,ms:Ce(e[t])})).sort((e,t)=>e.ms-t.ms);return X.set(e,n),n}function Z(e,n,r={},i=t.Now.instant()){let a=m(e,r).epochMilliseconds-i.epochMilliseconds;for(let{key:e,ms:t}of Se(n))if(a<=t)return e;return null}function Ce(e){let n=t.Duration.from(e);return(n.years??0)*12*p+(n.months??0)*p+(n.weeks??0)*7*864e5+(n.days??0)*864e5+(n.hours??0)*36e5+(n.minutes??0)*6e4+(n.seconds??0)*1e3+(n.milliseconds??0)+(n.microseconds??0)/1e3+(n.nanoseconds??0)/1e6}var we=[{field:`years`,unit:`year`},{field:`months`,unit:`month`},{field:`weeks`,unit:`week`},{field:`days`,unit:`day`},{field:`hours`,unit:`hour`},{field:`minutes`,unit:`minute`},{field:`seconds`,unit:`second`},{field:`milliseconds`,unit:`millisecond`}];function Q(e,n){return t.ZonedDateTime.compare(e,n)<=0?n.since(e,{largestUnit:`year`}):e.since(n,{largestUnit:`year`})}function $(e){for(let{field:t,unit:n}of we){let r=Math.abs(e[t]);if(r>0)return{unit:n,value:r}}return{unit:`millisecond`,value:0}}function Te(e,n,r={}){let i=n??t.Now.instant();if(!r.tz&&e instanceof t.Instant&&i instanceof t.Instant)return $(Q(e.toZonedDateTimeISO(`UTC`),i.toZonedDateTimeISO(`UTC`)));let a=u([e,i],r);return $(Q(h(e,{tz:a}),h(i,{tz:a})))}function Ee(e,n,i,a={}){let o=l(e,a),s=h(e,{...a,tz:o}),c=h(n,{...a,tz:o});if(t.ZonedDateTime.compare(s.add(i),s)<=0)throw new r(`dateRange: step must advance the date forward`);return De(s,c,i)}function*De(e,n,r){let i=e;for(;t.ZonedDateTime.compare(i,n)<=0;)yield i,i=i.add(r)}function Oe(e,t,n={}){let{count:r,frequency:i,interval:a=1,until:o}=t,s=l(e,n),c=i===`daily`?{days:a}:i===`weekly`?{weeks:a}:i===`monthly`?{months:a}:{years:a},u=o===void 0?void 0:m(o,{...n,tz:s});return ke(h(e,{...n,tz:s}),c,r,u)}function*ke(e,n,r,i){let a=e,o=0;for(;!(r!==void 0&&o>=r||i!==void 0&&t.Instant.compare(a.toInstant(),i)>0);)yield a,o++,a=a.add(n)}export{n as TempoError,r as TempoInvalidInputError,i as TempoInvalidTzError,a as TempoMissingTzError,o as TempoUnsupportedInputError,e as Temporal,j as clamp,Ee as dateRange,S as difference,re as endOf,Z as expires,pe as format,ye as formatDuration,ge as formatInstant,be as formatParts,me as formatRange,he as formatRangeParts,ve as formatRelative,_e as formatZoned,xe as humanize,ee as inTz,oe as isAfter,ae as isBefore,se as isSame,C as isValid,g as now,_ as nowInstant,w as parse,Y as parseDuration,x as parseInstant,y as parsePlainDate,b as parsePlainDateTime,v as parseZoned,Oe as recurrence,te as shift,ne as startOf,Te as timeDiff,m as toInstant,ce as within};
2
+ //# sourceMappingURL=tempo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tempo.js","names":[],"sources":["../src/errors.ts","../src/_tz.ts","../src/_convert.ts","../src/core.ts","../src/_floor.ts","../src/boundary.ts","../src/compare.ts","../src/format.ts","../src/classify.ts","../src/range.ts"],"sourcesContent":["/** Base class for all tempo errors. Use `instanceof TempoError` to catch any tempo-originated error. */\nexport class TempoError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is TempoError {\n return err instanceof TempoError;\n }\n}\n\n/** Thrown when a date/time input string or value cannot be parsed. */\nexport class TempoInvalidInputError extends TempoError {}\n\n/** Thrown when the provided timezone identifier is unknown or invalid. */\nexport class TempoInvalidTzError extends TempoError {}\n\n/** Thrown when an operation requires a timezone but none was supplied. */\nexport class TempoMissingTzError extends TempoError {}\n\n/** Thrown when an input type is not supported by the called operation. */\nexport class TempoUnsupportedInputError extends TempoError {}\n\n// ─── Error helpers ────────────────────────────────────────────────────────────\n\ntype TempoErrorCtor = new (message: string) => TempoError;\n\nexport function fail(message: string, Class: TempoErrorCtor = TempoInvalidInputError): never {\n throw new Class(message);\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, TimeInput } from './types';\n\nimport { TempoInvalidTzError, TempoMissingTzError, fail } from './errors';\n\n// ─── Timezone validation ──────────────────────────────────────────────────────\n\nexport function validateTz(tz: string): string {\n try {\n Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(tz);\n } catch {\n fail(\n `Unknown or invalid timezone: \"${tz}\". Expected an IANA timezone name (e.g. \"America/New_York\") or UTC offset (e.g. \"+05:30\").`,\n TempoInvalidTzError,\n );\n }\n\n return tz;\n}\n\n// ─── Timezone inference ───────────────────────────────────────────────────────\n\nexport function inferTimeZone(input: TimeInput, options: { tz?: string }): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n if (!tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return validateTz(tz);\n}\n\nexport function inferSharedTimeZone(inputs: TimeInput[], options: { tz?: string }): string {\n if (options.tz) return validateTz(options.tz);\n\n let inferred: string | undefined;\n\n for (const input of inputs) {\n if (!(input instanceof Temporal.ZonedDateTime)) continue;\n\n const tz = input.timeZoneId;\n\n if (!inferred) {\n inferred = tz;\n continue;\n }\n\n if (inferred !== tz) {\n fail('Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.');\n }\n }\n\n if (!inferred)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return inferred;\n}\n\n// ─── Range normalization ──────────────────────────────────────────────────────\n\nexport function normalizeRange(start: Temporal.Instant, end: Temporal.Instant): [Temporal.Instant, Temporal.Instant] {\n return Temporal.Instant.compare(start, end) <= 0 ? [start, end] : [end, start];\n}\n\n// ─── Shared constants ─────────────────────────────────────────────────────────\n\n/** Units that require timezone-aware context for calendar-accurate operations. */\nexport const CALENDAR_UNITS = new Set<CalendarUnit>(['day', 'month', 'week', 'year']);\n\n/** Approximate millisecond constants for threshold arithmetic. */\nexport const MS_PER_MONTH = 30.4375 * 86_400_000; // 365.25 / 12 days × 86400 s × 1000\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { DateTimeDisambiguation, TimeInput } from './types';\n\nimport { validateTz } from './_tz';\nimport { TempoMissingTzError, TempoUnsupportedInputError, fail } from './errors';\n\ntype WithPrefer = { prefer?: DateTimeDisambiguation };\ntype TimeOptionsWithTz = { tz: string };\n\n// ─── Direct resolution ────────────────────────────────────────────────────────\n\n/**\n * Converts any {@link TimeInput} to an absolute `Instant`.\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n */\nexport function toInstant(input: TimeInput, options: WithPrefer & { tz?: string } = {}): Temporal.Instant {\n if (input instanceof Temporal.Instant) return input;\n\n if (input instanceof Temporal.ZonedDateTime) return input.toInstant();\n\n if (input instanceof Temporal.PlainDateTime) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input\n .toZonedDateTime(validateTz(options.tz), {\n disambiguation: options.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n })\n .toInstant();\n }\n\n if (input instanceof Temporal.PlainDate) {\n if (!options.tz)\n fail('This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.', TempoMissingTzError);\n\n return input.toZonedDateTime({ timeZone: validateTz(options.tz) }).toInstant();\n }\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into `options.tz` as a `ZonedDateTime`.\n *\n * When `input` is already a `ZonedDateTime`, it is **re-projected** into `options.tz`\n * via `withTimeZone()` — the wall-clock time changes but the absolute instant is preserved.\n *\n * @example\n * ```ts\n * // Re-projection: same instant, different wall-clock\n * toZoned(parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]'), { tz: 'UTC' })\n * // 2026-03-21T10:00:00+00:00[UTC] ← wall-clock changed from 11:00 → 10:00\n * ```\n */\nexport function toZoned(\n input: TimeInput,\n options: TimeOptionsWithTz | (WithPrefer & { tz: string }),\n): Temporal.ZonedDateTime {\n const opts = options as TimeOptionsWithTz & WithPrefer;\n const tz = validateTz(opts.tz);\n\n if (input instanceof Temporal.ZonedDateTime) return input.withTimeZone(tz);\n\n if (input instanceof Temporal.PlainDateTime) {\n return input.toZonedDateTime(tz, {\n disambiguation: opts.prefer as 'compatible' | 'earlier' | 'later' | 'reject' | undefined,\n });\n }\n\n if (input instanceof Temporal.PlainDate) {\n return input.toZonedDateTime({ timeZone: tz });\n }\n\n if (input instanceof Temporal.Instant) return input.toZonedDateTimeISO(tz);\n\n fail(`Unsupported time input type: ${String(input)}`, TempoUnsupportedInputError);\n}\n\n/**\n * Projects any {@link TimeInput} into a specific timezone as a `ZonedDateTime`.\n * Unlike {@link toZoned}, this is the clean public API: explicit `tz` string parameter\n * rather than an options bag, signalling intent clearly.\n *\n * When `input` is already a `ZonedDateTime`, it is re-projected — same instant, new zone.\n *\n * @example\n * ```ts\n * inTz(parseInstant('2026-03-21T10:00:00Z'), 'Europe/Berlin')\n * // 2026-03-21T11:00:00+01:00[Europe/Berlin]\n * ```\n */\nexport function inTz(input: TimeInput, tz: string): Temporal.ZonedDateTime {\n return toZoned(input, { tz });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { CalendarUnit, DifferenceOptions, ParseAs, ShiftOptions, TimeInput } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { CALENDAR_UNITS, inferSharedTimeZone, inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\ntype TimeOptionsWithTz = { tz: string };\n\n/**\n * Returns the current date and time in the given timezone.\n *\n * @example\n * ```ts\n * now('America/New_York').hour; // current hour in New York\n * ```\n */\nexport function now(tz: string): Temporal.ZonedDateTime {\n return Temporal.Now.zonedDateTimeISO(tz);\n}\n\n/**\n * Returns the current absolute instant (UTC point in time).\n * Use this instead of `Temporal.Now.instant()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * timeDiff(nowInstant()) // { unit: 'millisecond', value: 0 } (compared to now)\n * expires(nowInstant(), { expired: { days: 0 }, safe: { years: 100 } }) // 'safe'\n * ```\n */\nexport function nowInstant(): Temporal.Instant {\n return Temporal.Now.instant();\n}\n\n/**\n * Parses a full ISO 8601 zoned date-time string into a `ZonedDateTime`.\n * Use this instead of `Temporal.ZonedDateTime.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]')\n * parseZoned('2026-03-21T00:00:00[UTC]')\n * ```\n */\nexport function parseZoned(input: string): Temporal.ZonedDateTime {\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n fail(\n `Invalid zoned date-time string: \"${input}\". Expected an ISO 8601 string with offset and timezone (e.g. 2026-03-21T10:00:00+01:00[Europe/Berlin]).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 date-only string into a timezone-free `PlainDate`.\n * Use this instead of `Temporal.PlainDate.from()` to avoid importing Temporal directly.\n *\n * @example\n * ```ts\n * parsePlainDate('2026-03-21') // 2026-03-21\n * ```\n */\nexport function parsePlainDate(input: string): Temporal.PlainDate {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n fail(`Invalid plain date string: \"${input}\". Expected an ISO 8601 date string (e.g. YYYY-MM-DD).`);\n }\n}\n\n/**\n * Parses an ISO 8601 string into a timezone-free `PlainDateTime` (wall-clock time).\n * Use {@link toInstant} or {@link inTz} to attach a timezone when needed.\n *\n * @example\n * ```ts\n * parsePlainDateTime('2026-03-21') // 2026-03-21T00:00:00\n * parsePlainDateTime('2026-03-21T10:15:30') // 2026-03-21T10:15:30\n * ```\n */\nexport function parsePlainDateTime(input: string): Temporal.PlainDateTime {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n fail(\n `Invalid date/time string: \"${input}\". Expected an ISO 8601 date or date-time string (e.g. YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss).`,\n );\n }\n}\n\n/**\n * Parses an ISO 8601 UTC string into an absolute `Instant`.\n *\n * @example\n * ```ts\n * parseInstant('2026-03-21T10:15:30Z')\n * ```\n */\nexport function parseInstant(input: string): Temporal.Instant {\n try {\n return Temporal.Instant.from(input);\n } catch {\n fail(`Invalid instant string: \"${input}\". Expected an ISO 8601 UTC string (e.g. YYYY-MM-DDTHH:mm:ssZ).`);\n }\n}\n\n/**\n * DST-safe date arithmetic. Adds `duration` to `input` and returns the result as a\n * `ZonedDateTime`. Handles spring-forward and fall-back correctly.\n *\n * **Always returns a `ZonedDateTime`** — even when the input is an `Instant`.\n * Call `.toInstant()` on the result if you need an `Instant` back.\n * Requires `options.tz` when input is an `Instant`, `PlainDate`, or `PlainDateTime`.\n *\n * @example\n * ```ts\n * shift(parseZoned('2026-03-08T01:30:00-05:00[America/New_York]'), { hours: 1 })\n * // 2026-03-08T03:30:00-04:00[America/New_York] (skipped the missing hour)\n *\n * // Instant input — tz required, result is ZonedDateTime\n * shift(parseInstant('2026-03-21T10:00:00Z'), { hours: 2 }, { tz: 'UTC' }).toInstant()\n * ```\n */\nexport function shift(\n input: Temporal.ZonedDateTime,\n duration: Temporal.DurationLike,\n options?: ShiftOptions,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: Temporal.Instant | Temporal.PlainDate | Temporal.PlainDateTime,\n duration: Temporal.DurationLike,\n options: ShiftOptions & TimeOptionsWithTz,\n): Temporal.ZonedDateTime;\nexport function shift(\n input: TimeInput,\n duration: Temporal.DurationLike,\n options: ShiftOptions = {},\n): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { prefer: options.prefer, tz }).add(duration);\n}\n\n/**\n * Returns the calendar-aware duration between `start` and `end`.\n *\n * When both inputs are `Instant` and no calendar unit is requested, the fast\n * path skips timezone conversion. Calendar units (`day`, `week`, `month`, `year`)\n * always require a timezone — pass `options.tz` or use `ZonedDateTime` inputs.\n * `options.prefer` (DST disambiguation) is only meaningful for `PlainDateTime` inputs.\n *\n * @example\n * ```ts\n * difference(\n * parseZoned('2026-03-08T00:00:00-05:00[America/New_York]'),\n * parseZoned('2026-03-09T00:00:00-04:00[America/New_York]'),\n * { largestUnit: 'hour' },\n * ).hours // 23 (DST spring-forward day)\n * ```\n */\nexport function difference(start: TimeInput, end: TimeInput, options: DifferenceOptions = {}): Temporal.Duration {\n const { largestUnit, prefer, roundingIncrement, roundingMode, smallestUnit } = options;\n const roundingOptions = { largestUnit, roundingIncrement, roundingMode, smallestUnit };\n\n const needsCalendar =\n (largestUnit !== undefined && CALENDAR_UNITS.has(largestUnit as CalendarUnit)) ||\n (smallestUnit !== undefined && CALENDAR_UNITS.has(smallestUnit as CalendarUnit));\n\n if (!needsCalendar && start instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return end.since(start, roundingOptions as Temporal.DifferenceOptions<Temporal.TimeUnit>);\n }\n\n const tz = inferSharedTimeZone([start, end], options);\n\n return toZoned(end, { prefer, tz }).since(toZoned(start, { prefer, tz }), roundingOptions);\n}\n\n/**\n * Type guard that checks whether `value` is a valid `TimeInput`.\n *\n * @example\n * ```ts\n * isValid(parseInstant('2026-03-21T10:00:00Z')) // true\n * isValid('2026-03-21') // false\n * ```\n */\nexport function isValid(value: unknown): value is TimeInput {\n return (\n value instanceof Temporal.Instant ||\n value instanceof Temporal.ZonedDateTime ||\n value instanceof Temporal.PlainDateTime ||\n value instanceof Temporal.PlainDate\n );\n}\n\n/**\n * Parses any ISO 8601 string into the most specific `TimeInput` type possible.\n * Tries ZonedDateTime → Instant → PlainDateTime → PlainDate in order.\n * Throws a descriptive `TypeError` if none match.\n *\n * Pass `as` to request a specific return type (throws if the string cannot be parsed as that type):\n *\n * @example\n * ```ts\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]') // TimeInput (auto-detect)\n * parse('2026-03-21T11:00:00+01:00[Europe/Berlin]', 'zoned') // Temporal.ZonedDateTime\n * parse('2026-03-21T10:00:00Z', 'instant') // Temporal.Instant\n * parse('2026-03-21T10:00:00', 'plain-datetime') // Temporal.PlainDateTime\n * parse('2026-03-21', 'plain-date') // Temporal.PlainDate\n * ```\n */\nexport function parse(input: string, as: 'zoned'): Temporal.ZonedDateTime;\nexport function parse(input: string, as: 'instant'): Temporal.Instant;\nexport function parse(input: string, as: 'plain-datetime'): Temporal.PlainDateTime;\nexport function parse(input: string, as: 'plain-date'): Temporal.PlainDate;\nexport function parse(input: string, as?: ParseAs): TimeInput;\nexport function parse(input: string, as?: ParseAs): TimeInput {\n if (as === 'zoned') return parseZoned(input);\n\n if (as === 'instant') return parseInstant(input);\n\n if (as === 'plain-datetime') return parsePlainDateTime(input);\n\n if (as === 'plain-date') return parsePlainDate(input);\n\n try {\n return Temporal.ZonedDateTime.from(input);\n } catch {\n /* try next format */\n }\n\n try {\n return Temporal.Instant.from(input);\n } catch {\n /* try next format */\n }\n\n // Try PlainDateTime before PlainDate — a date-only string (no 'T') will also\n // be accepted by PlainDateTime.from(), producing midnight, so we check the\n // string to pick the most specific type.\n if (input.includes('T')) {\n try {\n return Temporal.PlainDateTime.from(input);\n } catch {\n /* fall through to error */\n }\n } else {\n try {\n return Temporal.PlainDate.from(input);\n } catch {\n /* fall through to error */\n }\n }\n\n fail(\n `Unable to parse date/time string: \"${input}\". Expected ISO 8601 ZonedDateTime, Instant, PlainDateTime, or PlainDate.`,\n );\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, DateTimeDisambiguation, TimeInput } from './types';\n\nimport { toZoned } from './_convert';\n\n// ─── Floor-to-boundary-unit helper ───────────────────────────────────────────\n\nconst TIME_ZERO: Temporal.ZonedDateTimeLike = {\n hour: 0,\n microsecond: 0,\n millisecond: 0,\n minute: 0,\n nanosecond: 0,\n second: 0,\n};\n\nconst BOUNDARY_CLEAR: Record<Exclude<BoundaryUnit, 'week'>, Temporal.ZonedDateTimeLike> = {\n day: TIME_ZERO,\n hour: { microsecond: 0, millisecond: 0, minute: 0, nanosecond: 0, second: 0 },\n minute: { microsecond: 0, millisecond: 0, nanosecond: 0, second: 0 },\n month: { ...TIME_ZERO, day: 1 },\n year: { ...TIME_ZERO, day: 1, month: 1 },\n};\n\n/**\n * Floors `input` to the start of `unit` in `tz`, returning an `Instant`.\n * Used internally by both `boundary.ts` and `compare.ts` without either depending on the other.\n */\nexport function floorToUnit(\n input: TimeInput,\n unit: BoundaryUnit,\n options: { prefer?: DateTimeDisambiguation; tz: string; weekStartsOn?: number },\n): Temporal.Instant {\n const zoned = toZoned(input, { prefer: options.prefer, tz: options.tz });\n\n if (unit === 'week') {\n const daysToSubtract = (zoned.dayOfWeek - (options.weekStartsOn ?? 1) + 7) % 7;\n\n return zoned.subtract({ days: daysToSubtract }).with(TIME_ZERO).toInstant();\n }\n\n return zoned.with(BOUNDARY_CLEAR[unit]).toInstant();\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryOptions, BoundaryUnit, TimeInput } from './types';\n\nimport { floorToUnit } from './_floor';\nimport { inferTimeZone } from './_tz';\n\n// ─── Boundary step durations ──────────────────────────────────────────────────\n\nconst BOUNDARY_STEP: Record<BoundaryUnit, Temporal.DurationLike> = {\n day: { days: 1 },\n hour: { hours: 1 },\n minute: { minutes: 1 },\n month: { months: 1 },\n week: { weeks: 1 },\n year: { years: 1 },\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns the start of the given `unit` in the inferred or explicit timezone.\n *\n * @example\n * ```ts\n * startOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T00:00:00+00:00[UTC]\n *\n * startOf(instant, 'week', { tz: 'UTC', weekStartsOn: 1 })\n * // Monday of the current week\n * ```\n */\nexport function startOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n\n return floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn }).toZonedDateTimeISO(tz);\n}\n\n/**\n * Returns the last nanosecond of the given `unit` (exactly 1 ns before the next unit starts).\n *\n * @example\n * ```ts\n * endOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })\n * // 2026-03-21T23:59:59.999999999+00:00[UTC]\n * ```\n */\nexport function endOf(input: TimeInput, unit: BoundaryUnit, options: BoundaryOptions = {}): Temporal.ZonedDateTime {\n const tz = inferTimeZone(input, options);\n const startInstant = floorToUnit(input, unit, { tz, weekStartsOn: options.weekStartsOn });\n\n return startInstant.toZonedDateTimeISO(tz).add(BOUNDARY_STEP[unit]).subtract({ nanoseconds: 1 });\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { BoundaryUnit, CompareOptions, TimeInput } from './types';\n\nimport { toInstant } from './_convert';\nimport { floorToUnit } from './_floor';\nimport { inferSharedTimeZone, normalizeRange } from './_tz';\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveFlooredPair(\n a: TimeInput,\n b: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { left: Temporal.Instant; right: Temporal.Instant } {\n const tz = inferSharedTimeZone([a, b], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n\n return {\n left: floorToUnit(a, unit, unitOpts),\n right: floorToUnit(b, unit, unitOpts),\n };\n}\n\nfunction resolveFlooredTriple(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n unit: BoundaryUnit,\n options: CompareOptions,\n): { lower: Temporal.Instant; target: Temporal.Instant; upper: Temporal.Instant } {\n const tz = inferSharedTimeZone([value, start, end], options);\n const unitOpts = { tz, weekStartsOn: options.weekStartsOn };\n const target = floorToUnit(value, unit, unitOpts);\n const [lower, upper] = normalizeRange(floorToUnit(start, unit, unitOpts), floorToUnit(end, unit, unitOpts));\n\n return { lower, target, upper };\n}\n\nfunction compareByUnit(a: TimeInput, b: TimeInput, options: CompareOptions): number {\n if (!options.unit) {\n return Temporal.Instant.compare(toInstant(a, options), toInstant(b, options));\n }\n\n const { left, right } = resolveFlooredPair(a, b, options.unit, options);\n\n return Temporal.Instant.compare(left, right);\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns `true` when `a` is strictly before `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isBefore(parseInstant('2026-03-21T10:00:00Z'), parseInstant('2026-03-21T11:00:00Z'))\n * // true\n * ```\n */\nexport function isBefore(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) < 0;\n}\n\n/**\n * Returns `true` when `a` is strictly after `b` on the timeline.\n * Pass `options.unit` to compare by calendar boundary (e.g. same day).\n *\n * @example\n * ```ts\n * isAfter(parseInstant('2026-03-21T11:00:00Z'), parseInstant('2026-03-21T10:00:00Z'))\n * // true\n * ```\n */\nexport function isAfter(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) > 0;\n}\n\n/**\n * Returns `true` when `a` and `b` represent the same point (or boundary unit) in time.\n *\n * @example\n * ```ts\n * isSame(a, b, { tz: 'America/New_York', unit: 'day' })\n * // true when a and b fall on the same calendar day in New York\n * ```\n */\nexport function isSame(a: TimeInput, b: TimeInput, options: CompareOptions = {}): boolean {\n return compareByUnit(a, b, options) === 0;\n}\n\n/**\n * Returns `true` when `value` falls within `[start, end]` (inclusive, bounds normalized).\n * Pass `options.unit` to floor all three inputs to a calendar boundary before comparing.\n *\n * @example\n * ```ts\n * within(\n * parseInstant('2026-03-21T11:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ) // true\n * ```\n */\nexport function within(value: TimeInput, start: TimeInput, end: TimeInput, options: CompareOptions = {}): boolean {\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n return Temporal.Instant.compare(lower, target) <= 0 && Temporal.Instant.compare(target, upper) <= 0;\n}\n\n/**\n * Clamps `value` to within `[start, end]` (bounds normalized).\n *\n * When `value` is a `ZonedDateTime`, returns a `ZonedDateTime` in the same timezone.\n * Otherwise returns an `Instant`.\n *\n * When `options.unit` is set, all three inputs are floored to that calendar boundary before\n * clamping — the result is at the start of the boundary unit, not the original time-of-day.\n *\n * @example\n * ```ts\n * clamp(\n * parseInstant('2026-03-21T13:00:00Z'),\n * parseInstant('2026-03-21T10:00:00Z'),\n * parseInstant('2026-03-21T12:00:00Z'),\n * ).toString() // '2026-03-21T12:00:00Z'\n *\n * clamp(\n * parseZoned('2026-03-21T13:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T10:00:00+00:00[UTC]'),\n * parseZoned('2026-03-21T12:00:00+00:00[UTC]'),\n * ).toString() // '2026-03-21T12:00:00+00:00[UTC]'\n * ```\n */\nexport function clamp(\n value: Temporal.ZonedDateTime,\n start: TimeInput,\n end: TimeInput,\n options?: CompareOptions,\n): Temporal.ZonedDateTime;\nexport function clamp(value: TimeInput, start: TimeInput, end: TimeInput, options?: CompareOptions): Temporal.Instant;\nexport function clamp(\n value: TimeInput,\n start: TimeInput,\n end: TimeInput,\n options: CompareOptions = {},\n): Temporal.Instant | Temporal.ZonedDateTime {\n const isZoned = value instanceof Temporal.ZonedDateTime;\n const tz = isZoned ? value.timeZoneId : undefined;\n\n if (!options.unit) {\n const target = toInstant(value, options);\n const [lower, upper] = normalizeRange(toInstant(start, options), toInstant(end, options));\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n return isZoned && tz ? clamped.toZonedDateTimeISO(tz) : clamped;\n }\n\n const { lower, target, upper } = resolveFlooredTriple(value, start, end, options.unit, options);\n\n let clamped: Temporal.Instant;\n\n if (Temporal.Instant.compare(target, lower) < 0) clamped = lower;\n else if (Temporal.Instant.compare(target, upper) > 0) clamped = upper;\n else clamped = target;\n\n // For unit-based clamping, resolve the output tz from options or from value's zone\n const outTz = options.tz ?? tz ?? inferSharedTimeZone([value, start, end], options);\n\n return isZoned ? clamped.toZonedDateTimeISO(outTz) : clamped;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type {\n DurationFormatOptions,\n FormatOptions,\n FormatPattern,\n RelativeFormatOptions,\n RelativeTimeInput,\n TimeDiffResult,\n TimeInput,\n TimeOptions,\n} from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { fail } from './errors';\n\n// ─── Formatter types ──────────────────────────────────────────────────────────\n\ntype DurationFormatter = { format(value: Temporal.Duration): string };\ntype DurationFormatterConstructor = new (\n locales?: Intl.LocalesArgument,\n options?: { style?: 'digital' | 'long' | 'narrow' | 'short' },\n) => DurationFormatter;\n\n// ─── Formatter caches ─────────────────────────────────────────────────────────\n\nconst FORMATTER_CACHE_MAX = 128;\n\nfunction cappedGetOrCreate<V>(cache: Map<string, V>, key: string, factory: () => V): V {\n const cached = cache.get(key);\n\n if (cached !== undefined) return cached;\n\n if (cache.size >= FORMATTER_CACHE_MAX) {\n const oldest = cache.keys().next().value;\n\n if (oldest !== undefined) cache.delete(oldest);\n }\n\n const value = factory();\n\n cache.set(key, value);\n\n return value;\n}\n\nconst DATE_TIME_FORMATTER_CACHE = new Map<string, Intl.DateTimeFormat>();\nconst RELATIVE_TIME_FORMATTER_CACHE = new Map<string, Intl.RelativeTimeFormat>();\nconst DURATION_FORMATTER_CACHE = new Map<string, DurationFormatter>();\n\n// ─── Format presets ───────────────────────────────────────────────────────────\n\nconst FORMAT_PRESETS: Record<FormatPattern, Intl.DateTimeFormatOptions> = {\n 'date-only': { dateStyle: 'short' },\n long: { dateStyle: 'full', timeStyle: 'long' },\n medium: { dateStyle: 'medium', timeStyle: 'short' },\n short: { dateStyle: 'short', timeStyle: 'short' },\n 'time-only': { timeStyle: 'short' },\n};\n\n// ─── Formatter factory helpers ────────────────────────────────────────────────\n\nfunction serializeIntlOptions(options: Intl.DateTimeFormatOptions): string {\n return JSON.stringify(\n Object.entries(options)\n .filter(([, value]) => value !== undefined)\n .sort(([l], [r]) => l.localeCompare(r))\n .map(([key, value]) => [key, String(value)]),\n );\n}\n\nfunction makeFormatter(options: FormatOptions, fallbackTz?: string): Intl.DateTimeFormat {\n const tz = options.tz ?? fallbackTz;\n const locale = options.locale;\n\n if (options.intl !== undefined) {\n const cacheKey = `${String(locale ?? '')}|intl|${tz ?? ''}|${serializeIntlOptions(options.intl)}`;\n\n return cappedGetOrCreate(DATE_TIME_FORMATTER_CACHE, cacheKey, () => {\n const intlOptions = tz !== undefined ? { ...options.intl, timeZone: tz } : options.intl;\n\n return new Intl.DateTimeFormat(locale, intlOptions);\n });\n }\n\n const pattern = options.pattern ?? 'medium';\n const cacheKey = `${String(locale ?? '')}|${pattern}|${tz ?? ''}`;\n\n return cappedGetOrCreate(\n DATE_TIME_FORMATTER_CACHE,\n cacheKey,\n () => new Intl.DateTimeFormat(locale, { ...FORMAT_PRESETS[pattern], timeZone: tz }),\n );\n}\n\nfunction getRelativeFormatter(options: {\n locale?: Intl.LocalesArgument;\n numeric?: Intl.RelativeTimeFormatNumeric;\n style?: Intl.RelativeTimeFormatStyle;\n}): Intl.RelativeTimeFormat {\n const cacheKey = `${String(options.locale ?? '')}|${options.numeric ?? 'auto'}|${options.style ?? 'long'}`;\n\n return cappedGetOrCreate(\n RELATIVE_TIME_FORMATTER_CACHE,\n cacheKey,\n () =>\n new Intl.RelativeTimeFormat(options.locale, {\n numeric: options.numeric ?? 'auto',\n style: options.style ?? 'long',\n }),\n );\n}\n\nfunction getDurationFormatter(options: {\n locale?: Intl.LocalesArgument;\n style?: 'digital' | 'long' | 'narrow' | 'short';\n}): DurationFormatter | null {\n const IntlWithDurationFormat = Intl as typeof Intl & { DurationFormat?: DurationFormatterConstructor };\n\n if (!IntlWithDurationFormat.DurationFormat) return null;\n\n const cacheKey = `${String(options.locale ?? '')}|${options.style ?? ''}`;\n\n return cappedGetOrCreate(\n DURATION_FORMATTER_CACHE,\n cacheKey,\n () => new IntlWithDurationFormat.DurationFormat!(options.locale, { style: options.style }),\n );\n}\n\n// ─── Time scale constants ─────────────────────────────────────────────────────\n\nconst SECONDS_PER_MINUTE = 60;\nconst SECONDS_PER_HOUR = 3_600;\nconst SECONDS_PER_DAY = 86_400;\nconst SECONDS_PER_WEEK = 604_800;\nconst SECONDS_PER_MONTH = 2_629_800; // ≈ 30.4375 days × 86400\nconst SECONDS_PER_YEAR = 31_557_600; // 365.25 days × 86400\n\n// ─── Relative time helpers ────────────────────────────────────────────────────\n\nconst RELATIVE_UNITS: ReadonlyArray<{ scale: number; thresholdToPromote: number; unit: Intl.RelativeTimeFormatUnit }> =\n [\n { scale: 1, thresholdToPromote: SECONDS_PER_MINUTE, unit: 'second' },\n { scale: SECONDS_PER_MINUTE, thresholdToPromote: SECONDS_PER_HOUR / SECONDS_PER_MINUTE, unit: 'minute' },\n { scale: SECONDS_PER_HOUR, thresholdToPromote: SECONDS_PER_DAY / SECONDS_PER_HOUR, unit: 'hour' },\n { scale: SECONDS_PER_DAY, thresholdToPromote: SECONDS_PER_WEEK / SECONDS_PER_DAY, unit: 'day' },\n { scale: SECONDS_PER_WEEK, thresholdToPromote: SECONDS_PER_MONTH / SECONDS_PER_WEEK, unit: 'week' },\n { scale: SECONDS_PER_MONTH, thresholdToPromote: 12, unit: 'month' },\n { scale: SECONDS_PER_YEAR, thresholdToPromote: Number.POSITIVE_INFINITY, unit: 'year' },\n ];\n\nfunction toRelativeUnit(seconds: number): { unit: Intl.RelativeTimeFormatUnit; value: number } {\n if (!Number.isFinite(seconds)) fail('formatRelative received a non-finite time difference.');\n\n const roundedSeconds = Math.round(seconds);\n\n for (const { scale, thresholdToPromote, unit } of RELATIVE_UNITS) {\n const value = Math.round(roundedSeconds / scale);\n\n if (Math.abs(value) < thresholdToPromote) return { unit, value };\n }\n\n return { unit: 'year', value: Math.round(roundedSeconds / SECONDS_PER_YEAR) };\n}\n\n// ─── Duration fallback renderer ───────────────────────────────────────────────\n\n// All English duration unit names follow the same pluralization rule: singular = plural.slice(0, -1)\nconst DURATION_UNITS = [\n 'years',\n 'months',\n 'weeks',\n 'days',\n 'hours',\n 'minutes',\n 'seconds',\n 'milliseconds',\n 'microseconds',\n 'nanoseconds',\n] as const satisfies ReadonlyArray<keyof Temporal.Duration>;\n\n// English-only fallback; runs only when Intl.DurationFormat is unavailable in the runtime.\nfunction buildDurationFallback(duration: Temporal.Duration): string {\n const parts: string[] = [];\n\n for (const unit of DURATION_UNITS) {\n const value = Math.abs(duration[unit] as number);\n\n if (value !== 0) parts.push(`${value} ${value === 1 ? unit.slice(0, -1) : unit}`);\n }\n\n return parts.length === 0 ? '0 seconds' : parts.join(', ');\n}\n\n// ─── Private helpers ──────────────────────────────────────────────────────────\n\n/**\n * Resolves a shared display timezone for two-input range functions.\n * Throws when both inputs are `ZonedDateTime` with different zones and no `options.tz` override.\n */\nfunction resolveRangeTz(start: TimeInput, end: TimeInput, options: FormatOptions, caller: string): string | undefined {\n if (options.tz) return options.tz;\n\n const startTz = start instanceof Temporal.ZonedDateTime ? start.timeZoneId : undefined;\n const endTz = end instanceof Temporal.ZonedDateTime ? end.timeZoneId : undefined;\n\n if (startTz && endTz && startTz !== endTz) {\n fail(`${caller} received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`);\n }\n\n return startTz ?? endTz;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Formats `input` using `Intl.DateTimeFormat`. Defaults to `pattern: 'medium'`.\n *\n * Pass `intl` for full `Intl.DateTimeFormatOptions` control (mutually exclusive with `pattern`).\n * The timezone is inferred from a `ZonedDateTime` input or from `options.tz`.\n *\n * @example\n * ```ts\n * format(parseInstant('2026-03-21T10:15:30Z'), { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:15'\n * ```\n */\nexport function format(input: TimeInput, options: FormatOptions = {}): string {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).format(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Formats a time span between `start` and `end` using `Intl.DateTimeFormat.formatRange`.\n *\n * @example\n * ```ts\n * formatRange(start, end, { locale: 'en-GB', pattern: 'short', tz: 'UTC' })\n * // '21/03/2026, 10:00 – 12:00'\n * ```\n */\nexport function formatRange(start: TimeInput, end: TimeInput, options: FormatOptions = {}): string {\n const tz = resolveRangeTz(start, end, options, 'formatRange');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRange(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Returns the raw `Intl.DateTimeRangeFormatPart[]` array for a time span, enabling\n * fine-grained rendering of range start, end, and shared parts separately.\n *\n * @example\n * ```ts\n * formatRangeParts(start, end, { locale: 'en-US', pattern: 'short', tz: 'UTC' })\n * // [{ type: 'month', value: '3', source: 'startRange' }, ...]\n * ```\n */\nexport function formatRangeParts(\n start: TimeInput,\n end: TimeInput,\n options: FormatOptions = {},\n): ReturnType<Intl.DateTimeFormat['formatRangeToParts']> {\n const tz = resolveRangeTz(start, end, options, 'formatRangeParts');\n const formatter = makeFormatter(options, tz);\n\n return formatter.formatRangeToParts(\n new Date(toInstant(start, { tz }).epochMilliseconds),\n new Date(toInstant(end, { tz }).epochMilliseconds),\n );\n}\n\n/**\n * Serializes `input` to a UTC ISO 8601 instant string (`2026-03-21T10:15:30Z`).\n * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * @example\n * ```ts\n * formatInstant(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T10:15:30Z'\n * ```\n */\nexport function formatInstant(input: TimeInput, options: TimeOptions = {}): string {\n return toInstant(input, options).toString();\n}\n\n/**\n * Serializes `input` to a zoned ISO 8601 string (`2026-03-21T11:15:30+01:00[Europe/Berlin]`).\n *\n * @param options.tz - Required when `input` is a `PlainDate` or `PlainDateTime`.\n * Inferred automatically from a `ZonedDateTime` or `Instant` input.\n *\n * @throws {TempoError} When `input` is a `PlainDate` or `PlainDateTime` and `options.tz` is omitted.\n *\n * @example\n * ```ts\n * formatZoned(parseInstant('2026-03-21T10:15:30Z'), { tz: 'Europe/Berlin' })\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]'\n *\n * formatZoned(parseZoned('2026-03-21T11:15:30+01:00[Europe/Berlin]'))\n * // '2026-03-21T11:15:30+01:00[Europe/Berlin]' (tz inferred)\n * ```\n */\nexport function formatZoned(input: TimeInput, options: TimeOptions = {}): string {\n const tz = inferTimeZone(input, options);\n\n return toZoned(input, { tz }).toString();\n}\n\n/**\n * Formats `input` relative to `options.base` (defaults to now) using `Intl.RelativeTimeFormat`.\n *\n * @example\n * ```ts\n * formatRelative(parseInstant('2026-03-21T12:00:00Z'), {\n * base: parseInstant('2026-03-21T10:00:00Z'),\n * locale: 'en-US',\n * numeric: 'always',\n * })\n * // 'in 2 hours'\n * ```\n */\nexport function formatRelative(input: RelativeTimeInput, options: RelativeFormatOptions = {}): string {\n const target = input instanceof Temporal.Instant ? input : input.toInstant();\n const base = options.base\n ? options.base instanceof Temporal.Instant\n ? options.base\n : options.base.toInstant()\n : Temporal.Now.instant();\n const differenceInSeconds = (target.epochMilliseconds - base.epochMilliseconds) / 1000;\n const { unit, value } = toRelativeUnit(differenceInSeconds);\n\n return getRelativeFormatter(options).format(value, unit);\n}\n\n/**\n * Parses an ISO duration string or `Temporal.DurationLike` into a `Temporal.Duration`.\n *\n * @example\n * ```ts\n * parseDuration('PT2H30M').toString() // 'PT2H30M'\n * parseDuration({ hours: 2, minutes: 30 }).toString() // 'PT2H30M'\n * ```\n */\nexport function parseDuration(input: string | Temporal.DurationLike): Temporal.Duration {\n try {\n return Temporal.Duration.from(input);\n } catch {\n fail(`Invalid duration input: \"${String(input)}\". Expected an ISO 8601 duration string or Temporal.DurationLike.`);\n }\n}\n\n/**\n * Formats a duration using `Intl.DurationFormat` when available, falling back to\n * a human-readable plain-English string.\n *\n * @example\n * ```ts\n * formatDuration('PT2H30M', { locale: 'en-US', style: 'long' })\n * // '2 hours, 30 minutes'\n * ```\n */\nexport function formatDuration(input: string | Temporal.DurationLike, options: DurationFormatOptions = {}): string {\n const duration = parseDuration(input);\n const formatter = getDurationFormatter(options);\n\n if (formatter) return formatter.format(duration);\n\n return buildDurationFallback(duration);\n}\n\n/**\n * Returns the raw `Intl.DateTimeFormatPart[]` array for `input`, enabling\n * custom rendering where individual parts (year, month, day, etc.) need\n * to be styled or composed differently.\n *\n * @example\n * ```ts\n * formatParts(parseInstant('2026-03-21T10:15:30Z'), { pattern: 'medium', tz: 'UTC' })\n * // [{ type: 'month', value: 'Mar' }, { type: 'literal', value: ' ' }, ...]\n * ```\n */\nexport function formatParts(input: TimeInput, options: FormatOptions = {}): Intl.DateTimeFormatPart[] {\n const tz = options.tz ?? (input instanceof Temporal.ZonedDateTime ? input.timeZoneId : undefined);\n\n return makeFormatter(options, tz).formatToParts(new Date(toInstant(input, { tz }).epochMilliseconds));\n}\n\n/**\n * Converts a `TimeDiffResult` to a human-readable string.\n * Uses the singular unit name when value is 1, plural (unit + 's') otherwise.\n *\n * Pass `options.locale` to localize the numeric part via `Intl.NumberFormat`.\n * Unit names remain English — for fully localized output use {@link formatRelative}\n * or {@link formatDuration} instead.\n *\n * @example\n * ```ts\n * humanize({ unit: 'day', value: 1 }) // '1 day'\n * humanize({ unit: 'day', value: 3 }) // '3 days'\n * humanize({ unit: 'day', value: 3 }, { locale: 'ar' }) // '٣ days'\n * humanize({ unit: 'millisecond', value: 0 }) // '0 milliseconds'\n * ```\n */\nexport function humanize(diff: TimeDiffResult, options: { locale?: Intl.LocalesArgument } = {}): string {\n const { unit, value } = diff;\n const formatted = options.locale ? new Intl.NumberFormat(options.locale).format(value) : String(value);\n\n return `${formatted} ${value === 1 ? unit : `${unit}s`}`;\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { TimeDiffResult, TimeDiffUnit, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferSharedTimeZone, MS_PER_MONTH } from './_tz';\n\n// ─── Threshold sort cache ─────────────────────────────────────────────────────\n\ntype SortedThreshold<K extends string> = { key: K; ms: number }[];\n\nconst THRESHOLD_SORT_CACHE = new WeakMap<object, SortedThreshold<string>>();\n\nfunction getSortedThresholds<K extends string>(thresholds: Record<K, Temporal.DurationLike>): SortedThreshold<K> {\n const cached = THRESHOLD_SORT_CACHE.get(thresholds);\n\n if (cached) return cached as SortedThreshold<K>;\n\n const sorted = (Object.keys(thresholds) as K[])\n .map((key) => ({ key, ms: durationToMs(thresholds[key]) }))\n .sort((a, b) => a.ms - b.ms);\n\n THRESHOLD_SORT_CACHE.set(thresholds, sorted);\n\n return sorted;\n}\n\n// ─── expires ─────────────────────────────────────────────────────────────────\n\n/**\n * Classifies a date into a user-defined bucket by comparing diff = date − now\n * against the provided thresholds (sorted ascending). Returns the key of the\n * first threshold the diff falls within, or `null` if no threshold matches.\n *\n * Thresholds accept negative durations to classify past dates. The function\n * requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.\n *\n * **Performance:** threshold objects are cached by reference in a `WeakMap`. Define\n * the threshold record at module scope (not inline) so sorting is performed only once\n * per unique object.\n *\n * @example\n * ```ts\n * expires(expiresAt, {\n * longExpired: { days: -30 }, // more than 30 days in the past\n * expired: { days: 0 }, // any past date\n * critical: { days: 3 }, // within 3 days\n * warning: { days: 14 }, // within 14 days\n * safe: { years: 100 }, // catch-all for far future\n * })\n * // → 'longExpired' | 'expired' | 'critical' | 'warning' | 'safe' | null\n * ```\n */\nexport function expires<K extends string>(\n date: TimeInput,\n thresholds: Record<K, Temporal.DurationLike>,\n options: TimeOptions = {},\n now = Temporal.Now.instant(),\n): K | null {\n const dateMs = toInstant(date, options).epochMilliseconds;\n const nowMs = now.epochMilliseconds;\n\n // diff is positive for future dates, negative for past dates (date − now)\n const diffMs = dateMs - nowMs;\n\n for (const { key, ms } of getSortedThresholds(thresholds)) {\n if (diffMs <= ms) return key;\n }\n\n return null;\n}\n\n/** Converts a `DurationLike` to approximate milliseconds for threshold comparison. */\nfunction durationToMs(duration: Temporal.DurationLike): number {\n const d = Temporal.Duration.from(duration);\n\n // Use approximate conversions — thresholds are human-defined boundaries, not calendar-precise.\n return (\n (d.years ?? 0) * 12 * MS_PER_MONTH +\n (d.months ?? 0) * MS_PER_MONTH +\n (d.weeks ?? 0) * 7 * 86_400_000 +\n (d.days ?? 0) * 86_400_000 +\n (d.hours ?? 0) * 3_600_000 +\n (d.minutes ?? 0) * 60_000 +\n (d.seconds ?? 0) * 1_000 +\n (d.milliseconds ?? 0) +\n (d.microseconds ?? 0) / 1_000 +\n (d.nanoseconds ?? 0) / 1_000_000\n );\n}\n\n// ─── timeDiff ─────────────────────────────────────────────────────────────────\n\nconst UNIT_ORDER: ReadonlyArray<{ field: keyof Temporal.Duration; unit: TimeDiffUnit }> = [\n { field: 'years', unit: 'year' },\n { field: 'months', unit: 'month' },\n { field: 'weeks', unit: 'week' },\n { field: 'days', unit: 'day' },\n { field: 'hours', unit: 'hour' },\n { field: 'minutes', unit: 'minute' },\n { field: 'seconds', unit: 'second' },\n { field: 'milliseconds', unit: 'millisecond' },\n];\n\nfunction sinceZoned(a: Temporal.ZonedDateTime, b: Temporal.ZonedDateTime): Temporal.Duration {\n return Temporal.ZonedDateTime.compare(a, b) <= 0\n ? b.since(a, { largestUnit: 'year' })\n : a.since(b, { largestUnit: 'year' });\n}\n\nfunction pickLargestUnit(duration: Temporal.Duration): TimeDiffResult {\n for (const { field, unit } of UNIT_ORDER) {\n const value = Math.abs(duration[field] as number);\n\n if (value > 0) return { unit, value };\n }\n\n return { unit: 'millisecond', value: 0 };\n}\n\n/**\n * Returns the absolute calendar-accurate difference between two dates as a\n * structured `{ unit, value }` in the largest meaningful unit.\n *\n * When `b` is omitted, the current instant is used.\n * Requires `options.tz` when inputs are `PlainDate`, `PlainDateTime`, or plain `Instant` with\n * calendar-unit precision. Throws when timezone cannot be inferred from inputs.\n *\n * @example\n * ```ts\n * timeDiff(\n * parseInstant('2026-01-01T00:00:00Z'),\n * parseInstant('2027-03-15T00:00:00Z'),\n * )\n * // { unit: 'year', value: 1 }\n * ```\n */\nexport function timeDiff(a: TimeInput, b?: TimeInput, options: TimeOptions = {}): TimeDiffResult {\n const end: TimeInput = b ?? Temporal.Now.instant();\n\n // Fast path: two Instants with no explicit tz — project to UTC for calendar-accurate units.\n // Instants are absolute and timezone-independent; UTC is the canonical calendar context.\n if (!options.tz && a instanceof Temporal.Instant && end instanceof Temporal.Instant) {\n return pickLargestUnit(sinceZoned(a.toZonedDateTimeISO('UTC'), end.toZonedDateTimeISO('UTC')));\n }\n\n // Plain inputs or calendar-accurate comparison require a timezone.\n const tz = inferSharedTimeZone([a, end], options);\n\n return pickLargestUnit(sinceZoned(toZoned(a, { tz }), toZoned(end, { tz })));\n}\n","import { Temporal } from '@js-temporal/polyfill';\n\nimport type { RecurrenceRule, TimeInput, TimeOptions } from './types';\n\nimport { toInstant, toZoned } from './_convert';\nimport { inferTimeZone } from './_tz';\nimport { TempoInvalidInputError } from './errors';\n\n/**\n * Lazily generates `ZonedDateTime` values between `start` and `end` (inclusive),\n * advancing by `step` on each iteration.\n *\n * Returns a generator — use `for...of` for lazy consumption or spread to collect\n * into an array: `[...dateRange(...)]`.\n *\n * @throws {RangeError} when `step` does not advance the date forward. Thrown eagerly at call time.\n *\n * Yields nothing when `start > end` (the generator terminates immediately).\n *\n * When `start` is a `ZonedDateTime`, the timezone is inferred from it. If `end` is in a\n * different timezone, it is silently re-projected into `start`'s timezone. Pass `options.tz`\n * explicitly to override.\n *\n * @example\n * ```ts\n * // Lazy — safe for large ranges\n * for (const day of dateRange(start, end, { days: 1 }, { tz: 'UTC' })) {\n * if (someCondition(day)) break;\n * }\n *\n * // Collect to array\n * const days = [...dateRange(start, end, { days: 1 }, { tz: 'UTC' })];\n *\n * // ZonedDateTime inputs — tz is inferred, no need to pass options\n * const days = [...dateRange(zdtStart, zdtEnd, { days: 1 })];\n * ```\n */\nexport function dateRange(\n start: TimeInput,\n end: TimeInput,\n step: Temporal.DurationLike,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const tz = inferTimeZone(start, options);\n const startZoned = toZoned(start, { ...options, tz });\n const endZoned = toZoned(end, { ...options, tz });\n\n // Eager validation — fires at call time, not on first iteration.\n if (Temporal.ZonedDateTime.compare(startZoned.add(step), startZoned) <= 0) {\n throw new TempoInvalidInputError('dateRange: step must advance the date forward');\n }\n\n return dateRangeGenerator(startZoned, endZoned, step);\n}\n\nfunction* dateRangeGenerator(\n start: Temporal.ZonedDateTime,\n end: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n\n while (Temporal.ZonedDateTime.compare(current, end) <= 0) {\n yield current;\n current = current.add(step);\n }\n}\n\n/**\n * Lazily generates `ZonedDateTime` occurrences according to a recurrence rule.\n *\n * Supports `daily`, `weekly`, `monthly`, and `yearly` frequencies with an optional\n * `interval` (defaults to `1`), `count` limit, and `until` boundary (inclusive).\n * The `RecurrenceRule` type enforces that at least one of `count` or `until` must be\n * provided — this is a compile-time guarantee for TypeScript callers.\n * Passing `count: 0` yields an empty sequence without error.\n *\n * @example\n * ```ts\n * // Every Monday for 4 weeks\n * const mondays = [...recurrence(start, { frequency: 'weekly', count: 4 }, { tz: 'UTC' })];\n *\n * // Bi-weekly until a deadline\n * for (const date of recurrence(start, { frequency: 'weekly', interval: 2, until: deadline }, { tz: 'UTC' })) {\n * schedule(date);\n * }\n *\n * // ZonedDateTime start — tz is inferred, no need to pass options\n * for (const date of recurrence(zdtStart, { frequency: 'daily', count: 7 })) {\n * schedule(date);\n * }\n * ```\n */\nexport function recurrence(\n start: TimeInput,\n rule: RecurrenceRule,\n options: TimeOptions = {},\n): Generator<Temporal.ZonedDateTime> {\n const { count, frequency, interval = 1, until } = rule;\n\n const tz = inferTimeZone(start, options);\n\n const step: Temporal.DurationLike =\n frequency === 'daily'\n ? { days: interval }\n : frequency === 'weekly'\n ? { weeks: interval }\n : frequency === 'monthly'\n ? { months: interval }\n : { years: interval };\n\n const endInstant = until !== undefined ? toInstant(until, { ...options, tz }) : undefined;\n\n return recurrenceGenerator(toZoned(start, { ...options, tz }), step, count, endInstant);\n}\n\nfunction* recurrenceGenerator(\n start: Temporal.ZonedDateTime,\n step: Temporal.DurationLike,\n count: number | undefined,\n endInstant: Temporal.Instant | undefined,\n): Generator<Temporal.ZonedDateTime> {\n let current = start;\n let emitted = 0;\n\n while (true) {\n if (count !== undefined && emitted >= count) break;\n\n if (endInstant !== undefined && Temporal.Instant.compare(current.toInstant(), endInstant) > 0) break;\n\n yield current;\n emitted++;\n current = current.add(step);\n }\n}\n"],"mappings":"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"}