@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
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # @vielzeug/tempo
2
+
3
+ > Temporal-powered parsing, timezone conversion, arithmetic (DST-safe), and Intl formatting for modern TypeScript.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@vielzeug/tempo)](https://www.npmjs.com/package/@vielzeug/tempo) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ <details>
8
+ <summary>Quick Reference</summary>
9
+
10
+ **Package:** `@vielzeug/tempo` &nbsp;·&nbsp; **Category:** Date-time
11
+
12
+ **Key exports:** `now`, `parsePlainDateTime`, `parseInstant`, `parse`, `toInstant`, `inTz`, `shift`, `difference`, `format`, `formatRelative`, `isBefore`, `isAfter`, `isSame`, `startOf`, `endOf`, `expires`, `timeDiff`, `recurrence`
13
+
14
+ **When to use:** Temporal-powered parsing, timezone conversion, arithmetic (DST-safe), and Intl formatting for modern TypeScript.
15
+
16
+ **Related:** [@vielzeug/arsenal](https://vielzeug.dev/arsenal/)
17
+
18
+ </details>
19
+
20
+ `@vielzeug/tempo` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
21
+
22
+ ## Installation
23
+
24
+ ```sh
25
+ pnpm add @vielzeug/tempo
26
+ npm install @vielzeug/tempo
27
+ yarn add @vielzeug/tempo
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ import { format, formatInstant, inTz, parsePlainDateTime, shift, toInstant } from '@vielzeug/tempo';
34
+
35
+ // Parse a wall-clock string (no timezone attached)
36
+ const localMeeting = parsePlainDateTime('2026-03-21T10:30:00');
37
+
38
+ // Convert to an absolute instant using the user's timezone
39
+ const meetingInstant = toInstant(localMeeting, { tz: 'America/New_York' });
40
+
41
+ // Project to a zoned view and subtract 15 minutes (DST-safe)
42
+ const meetingNY = inTz(meetingInstant, 'America/New_York');
43
+ const reminder = shift(meetingNY, { minutes: -15 });
44
+
45
+ // Format for display
46
+ console.log(format(reminder, { pattern: 'short', locale: 'en-US', tz: 'America/New_York' }));
47
+
48
+ // Format for APIs / logs (stable UTC instant string)
49
+ console.log(formatInstant(reminder));
50
+ ```
51
+
52
+ Since many function names (`now`, `shift`, `clamp`, `difference`, …) are common in application code, use a namespace import to avoid collisions while still getting full tree-shaking:
53
+
54
+ ```ts
55
+ import * as tempo from '@vielzeug/tempo';
56
+
57
+ tempo.now('UTC');
58
+ tempo.difference(start, end, { largestUnit: 'day', tz: 'UTC' });
59
+ tempo.format(meeting, { pattern: 'short', locale: 'en-US', tz: 'America/New_York' });
60
+ ```
61
+
62
+ ## Documentation
63
+
64
+ - [Overview](https://vielzeug.dev/tempo/)
65
+ - [Usage Guide](https://vielzeug.dev/tempo/usage)
66
+ - [API Reference](https://vielzeug.dev/tempo/api)
67
+ - [Examples](https://vielzeug.dev/tempo/examples)
68
+
69
+ ## License
70
+
71
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs"),t=require("./_tz.cjs");let n=require("@js-temporal/polyfill");function r(r,i={}){if(r instanceof n.Temporal.Instant)return r;if(r instanceof n.Temporal.ZonedDateTime)return r.toInstant();if(r instanceof n.Temporal.PlainDateTime)return i.tz||e.fail(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,e.TempoMissingTzError),r.toZonedDateTime(t.validateTz(i.tz),{disambiguation:i.prefer}).toInstant();if(r instanceof n.Temporal.PlainDate)return i.tz||e.fail(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,e.TempoMissingTzError),r.toZonedDateTime({timeZone:t.validateTz(i.tz)}).toInstant();e.fail(`Unsupported time input type: ${String(r)}`,e.TempoUnsupportedInputError)}function i(r,i){let a=i,o=t.validateTz(a.tz);if(r instanceof n.Temporal.ZonedDateTime)return r.withTimeZone(o);if(r instanceof n.Temporal.PlainDateTime)return r.toZonedDateTime(o,{disambiguation:a.prefer});if(r instanceof n.Temporal.PlainDate)return r.toZonedDateTime({timeZone:o});if(r instanceof n.Temporal.Instant)return r.toZonedDateTimeISO(o);e.fail(`Unsupported time input type: ${String(r)}`,e.TempoUnsupportedInputError)}function a(e,t){return i(e,{tz:t})}exports.inTz=a,exports.toInstant=r,exports.toZoned=i;
2
+ //# sourceMappingURL=_convert.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_convert.cjs","names":[],"sources":["../src/_convert.ts"],"sourcesContent":["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"],"mappings":"8FAgBA,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,EAAA,KAAK,oFAAqF,EAAA,mBAAmB,EAExG,EACJ,gBAAgB,EAAA,WAAW,EAAQ,EAAE,EAAG,CACvC,eAAgB,EAAQ,MAC1B,CAAC,CAAC,CACD,UAAU,EAGf,GAAI,aAAiB,EAAA,SAAS,UAI5B,OAHK,EAAQ,IACX,EAAA,KAAK,oFAAqF,EAAA,mBAAmB,EAExG,EAAM,gBAAgB,CAAE,SAAU,EAAA,WAAW,EAAQ,EAAE,CAAE,CAAC,CAAC,CAAC,UAAU,EAG/E,EAAA,KAAK,gCAAgC,OAAO,CAAK,IAAK,EAAA,0BAA0B,CAClF,CAeA,SAAgB,EACd,EACA,EACwB,CACxB,IAAM,EAAO,EACP,EAAK,EAAA,WAAW,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,EAAA,KAAK,gCAAgC,OAAO,CAAK,IAAK,EAAA,0BAA0B,CAClF,CAeA,SAAgB,EAAK,EAAkB,EAAoC,CACzE,OAAO,EAAQ,EAAO,CAAE,IAAG,CAAC,CAC9B"}
@@ -0,0 +1,47 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ import type { DateTimeDisambiguation, TimeInput } from './types';
3
+ type WithPrefer = {
4
+ prefer?: DateTimeDisambiguation;
5
+ };
6
+ type TimeOptionsWithTz = {
7
+ tz: string;
8
+ };
9
+ /**
10
+ * Converts any {@link TimeInput} to an absolute `Instant`.
11
+ * Requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.
12
+ */
13
+ export declare function toInstant(input: TimeInput, options?: WithPrefer & {
14
+ tz?: string;
15
+ }): Temporal.Instant;
16
+ /**
17
+ * Projects any {@link TimeInput} into `options.tz` as a `ZonedDateTime`.
18
+ *
19
+ * When `input` is already a `ZonedDateTime`, it is **re-projected** into `options.tz`
20
+ * via `withTimeZone()` — the wall-clock time changes but the absolute instant is preserved.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // Re-projection: same instant, different wall-clock
25
+ * toZoned(parseZoned('2026-03-21T11:00:00+01:00[Europe/Berlin]'), { tz: 'UTC' })
26
+ * // 2026-03-21T10:00:00+00:00[UTC] ← wall-clock changed from 11:00 → 10:00
27
+ * ```
28
+ */
29
+ export declare function toZoned(input: TimeInput, options: TimeOptionsWithTz | (WithPrefer & {
30
+ tz: string;
31
+ })): Temporal.ZonedDateTime;
32
+ /**
33
+ * Projects any {@link TimeInput} into a specific timezone as a `ZonedDateTime`.
34
+ * Unlike {@link toZoned}, this is the clean public API: explicit `tz` string parameter
35
+ * rather than an options bag, signalling intent clearly.
36
+ *
37
+ * When `input` is already a `ZonedDateTime`, it is re-projected — same instant, new zone.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * inTz(parseInstant('2026-03-21T10:00:00Z'), 'Europe/Berlin')
42
+ * // 2026-03-21T11:00:00+01:00[Europe/Berlin]
43
+ * ```
44
+ */
45
+ export declare function inTz(input: TimeInput, tz: string): Temporal.ZonedDateTime;
46
+ export {};
47
+ //# sourceMappingURL=_convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_convert.d.ts","sourceRoot":"","sources":["../src/_convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKjE,KAAK,UAAU,GAAG;IAAE,MAAM,CAAC,EAAE,sBAAsB,CAAA;CAAE,CAAC;AACtD,KAAK,iBAAiB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAIxC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,QAAQ,CAAC,OAAO,CAwBxG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,iBAAiB,GAAG,CAAC,UAAU,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,QAAQ,CAAC,aAAa,CAmBxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,aAAa,CAEzE"}
@@ -0,0 +1,26 @@
1
+ import { TempoMissingTzError as e, TempoUnsupportedInputError as t, fail as n } from "./errors.js";
2
+ import { validateTz as r } from "./_tz.js";
3
+ import { Temporal as i } from "@js-temporal/polyfill";
4
+ //#region src/_convert.ts
5
+ function a(a, o = {}) {
6
+ if (a instanceof i.Instant) return a;
7
+ if (a instanceof i.ZonedDateTime) return a.toInstant();
8
+ if (a instanceof i.PlainDateTime) return o.tz || n("This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.", e), a.toZonedDateTime(r(o.tz), { disambiguation: o.prefer }).toInstant();
9
+ if (a instanceof i.PlainDate) return o.tz || n("This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.", e), a.toZonedDateTime({ timeZone: r(o.tz) }).toInstant();
10
+ n(`Unsupported time input type: ${String(a)}`, t);
11
+ }
12
+ function o(e, a) {
13
+ let o = a, s = r(o.tz);
14
+ if (e instanceof i.ZonedDateTime) return e.withTimeZone(s);
15
+ if (e instanceof i.PlainDateTime) return e.toZonedDateTime(s, { disambiguation: o.prefer });
16
+ if (e instanceof i.PlainDate) return e.toZonedDateTime({ timeZone: s });
17
+ if (e instanceof i.Instant) return e.toZonedDateTimeISO(s);
18
+ n(`Unsupported time input type: ${String(e)}`, t);
19
+ }
20
+ function s(e, t) {
21
+ return o(e, { tz: t });
22
+ }
23
+ //#endregion
24
+ export { s as inTz, a as toInstant, o as toZoned };
25
+
26
+ //# sourceMappingURL=_convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_convert.js","names":[],"sources":["../src/_convert.ts"],"sourcesContent":["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"],"mappings":";;;;AAgBA,SAAgB,EAAU,GAAkB,IAAwC,CAAC,GAAqB;CACxG,IAAI,aAAiB,EAAS,SAAS,OAAO;CAE9C,IAAI,aAAiB,EAAS,eAAe,OAAO,EAAM,UAAU;CAEpE,IAAI,aAAiB,EAAS,eAI5B,OAHK,EAAQ,MACX,EAAK,qFAAqF,CAAmB,GAExG,EACJ,gBAAgB,EAAW,EAAQ,EAAE,GAAG,EACvC,gBAAgB,EAAQ,OAC1B,CAAC,CAAC,CACD,UAAU;CAGf,IAAI,aAAiB,EAAS,WAI5B,OAHK,EAAQ,MACX,EAAK,qFAAqF,CAAmB,GAExG,EAAM,gBAAgB,EAAE,UAAU,EAAW,EAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU;CAG/E,EAAK,gCAAgC,OAAO,CAAK,KAAK,CAA0B;AAClF;AAeA,SAAgB,EACd,GACA,GACwB;CACxB,IAAM,IAAO,GACP,IAAK,EAAW,EAAK,EAAE;CAE7B,IAAI,aAAiB,EAAS,eAAe,OAAO,EAAM,aAAa,CAAE;CAEzE,IAAI,aAAiB,EAAS,eAC5B,OAAO,EAAM,gBAAgB,GAAI,EAC/B,gBAAgB,EAAK,OACvB,CAAC;CAGH,IAAI,aAAiB,EAAS,WAC5B,OAAO,EAAM,gBAAgB,EAAE,UAAU,EAAG,CAAC;CAG/C,IAAI,aAAiB,EAAS,SAAS,OAAO,EAAM,mBAAmB,CAAE;CAEzE,EAAK,gCAAgC,OAAO,CAAK,KAAK,CAA0B;AAClF;AAeA,SAAgB,EAAK,GAAkB,GAAoC;CACzE,OAAO,EAAQ,GAAO,EAAE,MAAG,CAAC;AAC9B"}
@@ -0,0 +1,2 @@
1
+ const e=require("./_convert.cjs");require("@js-temporal/polyfill");var t={hour:0,microsecond:0,millisecond:0,minute:0,nanosecond:0,second:0},n={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 r(r,i,a){let o=e.toZoned(r,{prefer:a.prefer,tz:a.tz});if(i===`week`){let e=(o.dayOfWeek-(a.weekStartsOn??1)+7)%7;return o.subtract({days:e}).with(t).toInstant()}return o.with(n[i]).toInstant()}exports.floorToUnit=r;
2
+ //# sourceMappingURL=_floor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_floor.cjs","names":[],"sources":["../src/_floor.ts"],"sourcesContent":["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"],"mappings":"mEAQA,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,EAAA,QAAQ,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"}
@@ -0,0 +1,12 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ import type { BoundaryUnit, DateTimeDisambiguation, TimeInput } from './types';
3
+ /**
4
+ * Floors `input` to the start of `unit` in `tz`, returning an `Instant`.
5
+ * Used internally by both `boundary.ts` and `compare.ts` without either depending on the other.
6
+ */
7
+ export declare function floorToUnit(input: TimeInput, unit: BoundaryUnit, options: {
8
+ prefer?: DateTimeDisambiguation;
9
+ tz: string;
10
+ weekStartsOn?: number;
11
+ }): Temporal.Instant;
12
+ //# sourceMappingURL=_floor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_floor.d.ts","sourceRoot":"","sources":["../src/_floor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAuB/E;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9E,QAAQ,CAAC,OAAO,CAUlB"}
package/dist/_floor.js ADDED
@@ -0,0 +1,50 @@
1
+ import { toZoned as e } from "./_convert.js";
2
+ import "@js-temporal/polyfill";
3
+ //#region src/_floor.ts
4
+ var t = {
5
+ hour: 0,
6
+ microsecond: 0,
7
+ millisecond: 0,
8
+ minute: 0,
9
+ nanosecond: 0,
10
+ second: 0
11
+ }, n = {
12
+ day: t,
13
+ hour: {
14
+ microsecond: 0,
15
+ millisecond: 0,
16
+ minute: 0,
17
+ nanosecond: 0,
18
+ second: 0
19
+ },
20
+ minute: {
21
+ microsecond: 0,
22
+ millisecond: 0,
23
+ nanosecond: 0,
24
+ second: 0
25
+ },
26
+ month: {
27
+ ...t,
28
+ day: 1
29
+ },
30
+ year: {
31
+ ...t,
32
+ day: 1,
33
+ month: 1
34
+ }
35
+ };
36
+ function r(r, i, a) {
37
+ let o = e(r, {
38
+ prefer: a.prefer,
39
+ tz: a.tz
40
+ });
41
+ if (i === "week") {
42
+ let e = (o.dayOfWeek - (a.weekStartsOn ?? 1) + 7) % 7;
43
+ return o.subtract({ days: e }).with(t).toInstant();
44
+ }
45
+ return o.with(n[i]).toInstant();
46
+ }
47
+ //#endregion
48
+ export { r as floorToUnit };
49
+
50
+ //# sourceMappingURL=_floor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_floor.js","names":[],"sources":["../src/_floor.ts"],"sourcesContent":["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"],"mappings":";;;AAQA,IAAM,IAAwC;CAC5C,MAAM;CACN,aAAa;CACb,aAAa;CACb,QAAQ;CACR,YAAY;CACZ,QAAQ;AACV,GAEM,IAAoF;CACxF,KAAK;CACL,MAAM;EAAE,aAAa;EAAG,aAAa;EAAG,QAAQ;EAAG,YAAY;EAAG,QAAQ;CAAE;CAC5E,QAAQ;EAAE,aAAa;EAAG,aAAa;EAAG,YAAY;EAAG,QAAQ;CAAE;CACnE,OAAO;EAAE,GAAG;EAAW,KAAK;CAAE;CAC9B,MAAM;EAAE,GAAG;EAAW,KAAK;EAAG,OAAO;CAAE;AACzC;AAMA,SAAgB,EACd,GACA,GACA,GACkB;CAClB,IAAM,IAAQ,EAAQ,GAAO;EAAE,QAAQ,EAAQ;EAAQ,IAAI,EAAQ;CAAG,CAAC;CAEvE,IAAI,MAAS,QAAQ;EACnB,IAAM,KAAkB,EAAM,aAAa,EAAQ,gBAAgB,KAAK,KAAK;EAE7E,OAAO,EAAM,SAAS,EAAE,MAAM,EAAe,CAAC,CAAC,CAAC,KAAK,CAAS,CAAC,CAAC,UAAU;CAC5E;CAEA,OAAO,EAAM,KAAK,EAAe,EAAK,CAAC,CAAC,UAAU;AACpD"}
package/dist/_tz.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs");let t=require("@js-temporal/polyfill");function n(n){try{t.Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(n)}catch{e.fail(`Unknown or invalid timezone: "${n}". Expected an IANA timezone name (e.g. "America/New_York") or UTC offset (e.g. "+05:30").`,e.TempoInvalidTzError)}return n}function r(r,i){let a=i.tz??(r instanceof t.Temporal.ZonedDateTime?r.timeZoneId:void 0);return a||e.fail(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,e.TempoMissingTzError),n(a)}function i(r,i){if(i.tz)return n(i.tz);let a;for(let n of r){if(!(n instanceof t.Temporal.ZonedDateTime))continue;let r=n.timeZoneId;if(!a){a=r;continue}a!==r&&e.fail(`Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.`)}return a||e.fail(`This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.`,e.TempoMissingTzError),a}function a(e,n){return t.Temporal.Instant.compare(e,n)<=0?[e,n]:[n,e]}var o=new Set([`day`,`month`,`week`,`year`]),s=30.4375*864e5;exports.CALENDAR_UNITS=o,exports.MS_PER_MONTH=s,exports.inferSharedTimeZone=i,exports.inferTimeZone=r,exports.normalizeRange=a,exports.validateTz=n;
2
+ //# sourceMappingURL=_tz.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_tz.cjs","names":[],"sources":["../src/_tz.ts"],"sourcesContent":["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"],"mappings":"uEAQA,SAAgB,EAAW,EAAoB,CAC7C,GAAI,CACF,EAAA,SAAS,QAAQ,sBAAsB,CAAC,CAAC,CAAC,mBAAmB,CAAE,CACjE,MAAQ,CACN,EAAA,KACE,iCAAiC,EAAG,4FACpC,EAAA,mBACF,CACF,CAEA,OAAO,CACT,CAIA,SAAgB,EAAc,EAAkB,EAAkC,CAChF,IAAM,EAAK,EAAQ,KAAO,aAAiB,EAAA,SAAS,cAAgB,EAAM,WAAa,IAAA,IAKvF,OAHK,GACH,EAAA,KAAK,oFAAqF,EAAA,mBAAmB,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,EAAA,KAAK,iGAAiG,CAE1G,CAKA,OAHK,GACH,EAAA,KAAK,oFAAqF,EAAA,mBAAmB,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"}
package/dist/_tz.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ import type { CalendarUnit, TimeInput } from './types';
3
+ export declare function validateTz(tz: string): string;
4
+ export declare function inferTimeZone(input: TimeInput, options: {
5
+ tz?: string;
6
+ }): string;
7
+ export declare function inferSharedTimeZone(inputs: TimeInput[], options: {
8
+ tz?: string;
9
+ }): string;
10
+ export declare function normalizeRange(start: Temporal.Instant, end: Temporal.Instant): [Temporal.Instant, Temporal.Instant];
11
+ /** Units that require timezone-aware context for calendar-accurate operations. */
12
+ export declare const CALENDAR_UNITS: Set<CalendarUnit>;
13
+ /** Approximate millisecond constants for threshold arithmetic. */
14
+ export declare const MS_PER_MONTH: number;
15
+ //# sourceMappingURL=_tz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_tz.d.ts","sourceRoot":"","sources":["../src/_tz.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMvD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAW7C;AAID,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAOhF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAwBzF;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAEnH;AAID,kFAAkF;AAClF,eAAO,MAAM,cAAc,mBAA0D,CAAC;AAEtF,kEAAkE;AAClE,eAAO,MAAM,YAAY,QAAuB,CAAC"}
package/dist/_tz.js ADDED
@@ -0,0 +1,42 @@
1
+ import { TempoInvalidTzError as e, TempoMissingTzError as t, fail as n } from "./errors.js";
2
+ import { Temporal as r } from "@js-temporal/polyfill";
3
+ //#region src/_tz.ts
4
+ function i(t) {
5
+ try {
6
+ r.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(t);
7
+ } catch {
8
+ n(`Unknown or invalid timezone: "${t}". Expected an IANA timezone name (e.g. "America/New_York") or UTC offset (e.g. "+05:30").`, e);
9
+ }
10
+ return t;
11
+ }
12
+ function a(e, a) {
13
+ let o = a.tz ?? (e instanceof r.ZonedDateTime ? e.timeZoneId : void 0);
14
+ return o || n("This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.", t), i(o);
15
+ }
16
+ function o(e, a) {
17
+ if (a.tz) return i(a.tz);
18
+ let o;
19
+ for (let t of e) {
20
+ if (!(t instanceof r.ZonedDateTime)) continue;
21
+ let e = t.timeZoneId;
22
+ if (!o) {
23
+ o = e;
24
+ continue;
25
+ }
26
+ o !== e && n("Comparison received ZonedDateTime inputs with different time zones. Pass options.tz explicitly.");
27
+ }
28
+ return o || n("This operation requires a timezone. Pass options.tz or use a ZonedDateTime input.", t), o;
29
+ }
30
+ function s(e, t) {
31
+ return r.Instant.compare(e, t) <= 0 ? [e, t] : [t, e];
32
+ }
33
+ var c = /* @__PURE__ */ new Set([
34
+ "day",
35
+ "month",
36
+ "week",
37
+ "year"
38
+ ]), l = 30.4375 * 864e5;
39
+ //#endregion
40
+ export { c as CALENDAR_UNITS, l as MS_PER_MONTH, o as inferSharedTimeZone, a as inferTimeZone, s as normalizeRange, i as validateTz };
41
+
42
+ //# sourceMappingURL=_tz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_tz.js","names":[],"sources":["../src/_tz.ts"],"sourcesContent":["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"],"mappings":";;;AAQA,SAAgB,EAAW,GAAoB;CAC7C,IAAI;EACF,EAAS,QAAQ,sBAAsB,CAAC,CAAC,CAAC,mBAAmB,CAAE;CACjE,QAAQ;EACN,EACE,iCAAiC,EAAG,6FACpC,CACF;CACF;CAEA,OAAO;AACT;AAIA,SAAgB,EAAc,GAAkB,GAAkC;CAChF,IAAM,IAAK,EAAQ,OAAO,aAAiB,EAAS,gBAAgB,EAAM,aAAa,KAAA;CAKvF,OAHK,KACH,EAAK,qFAAqF,CAAmB,GAExG,EAAW,CAAE;AACtB;AAEA,SAAgB,EAAoB,GAAqB,GAAkC;CACzF,IAAI,EAAQ,IAAI,OAAO,EAAW,EAAQ,EAAE;CAE5C,IAAI;CAEJ,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAI,EAAE,aAAiB,EAAS,gBAAgB;EAEhD,IAAM,IAAK,EAAM;EAEjB,IAAI,CAAC,GAAU;GACb,IAAW;GACX;EACF;EAEA,AAAI,MAAa,KACf,EAAK,iGAAiG;CAE1G;CAKA,OAHK,KACH,EAAK,qFAAqF,CAAmB,GAExG;AACT;AAIA,SAAgB,EAAe,GAAyB,GAA6D;CACnH,OAAO,EAAS,QAAQ,QAAQ,GAAO,CAAG,KAAK,IAAI,CAAC,GAAO,CAAG,IAAI,CAAC,GAAK,CAAK;AAC/E;AAKA,IAAa,oBAAiB,IAAI,IAAkB;CAAC;CAAO;CAAS;CAAQ;AAAM,CAAC,GAGvE,IAAe,UAAU"}
@@ -0,0 +1,2 @@
1
+ const e=require("./_tz.cjs"),t=require("./_floor.cjs");require("@js-temporal/polyfill");var n={day:{days:1},hour:{hours:1},minute:{minutes:1},month:{months:1},week:{weeks:1},year:{years:1}};function r(n,r,i={}){let a=e.inferTimeZone(n,i);return t.floorToUnit(n,r,{tz:a,weekStartsOn:i.weekStartsOn}).toZonedDateTimeISO(a)}function i(r,i,a={}){let o=e.inferTimeZone(r,a);return t.floorToUnit(r,i,{tz:o,weekStartsOn:a.weekStartsOn}).toZonedDateTimeISO(o).add(n[i]).subtract({nanoseconds:1})}exports.endOf=i,exports.startOf=r;
2
+ //# sourceMappingURL=boundary.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.cjs","names":[],"sources":["../src/boundary.ts"],"sourcesContent":["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"],"mappings":"wFASA,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,EAAQ,EAAkB,EAAoB,EAA2B,CAAC,EAA2B,CACnH,IAAM,EAAK,EAAA,cAAc,EAAO,CAAO,EAEvC,OAAO,EAAA,YAAY,EAAO,EAAM,CAAE,KAAI,aAAc,EAAQ,YAAa,CAAC,CAAC,CAAC,mBAAmB,CAAE,CACnG,CAWA,SAAgB,EAAM,EAAkB,EAAoB,EAA2B,CAAC,EAA2B,CACjH,IAAM,EAAK,EAAA,cAAc,EAAO,CAAO,EAGvC,OAFqB,EAAA,YAAY,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"}
@@ -0,0 +1,26 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ import type { BoundaryOptions, BoundaryUnit, TimeInput } from './types';
3
+ /**
4
+ * Returns the start of the given `unit` in the inferred or explicit timezone.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * startOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })
9
+ * // 2026-03-21T00:00:00+00:00[UTC]
10
+ *
11
+ * startOf(instant, 'week', { tz: 'UTC', weekStartsOn: 1 })
12
+ * // Monday of the current week
13
+ * ```
14
+ */
15
+ export declare function startOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;
16
+ /**
17
+ * Returns the last nanosecond of the given `unit` (exactly 1 ns before the next unit starts).
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * endOf(parseInstant('2026-03-21T10:15:30Z'), 'day', { tz: 'UTC' })
22
+ * // 2026-03-21T23:59:59.999999999+00:00[UTC]
23
+ * ```
24
+ */
25
+ export declare function endOf(input: TimeInput, unit: BoundaryUnit, options?: BoundaryOptions): Temporal.ZonedDateTime;
26
+ //# sourceMappingURL=boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../src/boundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAkBxE;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAAC,aAAa,CAInH;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAAC,aAAa,CAKjH"}
@@ -0,0 +1,30 @@
1
+ import { inferTimeZone as e } from "./_tz.js";
2
+ import { floorToUnit as t } from "./_floor.js";
3
+ import "@js-temporal/polyfill";
4
+ //#region src/boundary.ts
5
+ var n = {
6
+ day: { days: 1 },
7
+ hour: { hours: 1 },
8
+ minute: { minutes: 1 },
9
+ month: { months: 1 },
10
+ week: { weeks: 1 },
11
+ year: { years: 1 }
12
+ };
13
+ function r(n, r, i = {}) {
14
+ let a = e(n, i);
15
+ return t(n, r, {
16
+ tz: a,
17
+ weekStartsOn: i.weekStartsOn
18
+ }).toZonedDateTimeISO(a);
19
+ }
20
+ function i(r, i, a = {}) {
21
+ let o = e(r, a);
22
+ return t(r, i, {
23
+ tz: o,
24
+ weekStartsOn: a.weekStartsOn
25
+ }).toZonedDateTimeISO(o).add(n[i]).subtract({ nanoseconds: 1 });
26
+ }
27
+ //#endregion
28
+ export { i as endOf, r as startOf };
29
+
30
+ //# sourceMappingURL=boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundary.js","names":[],"sources":["../src/boundary.ts"],"sourcesContent":["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"],"mappings":";;;;AASA,IAAM,IAA6D;CACjE,KAAK,EAAE,MAAM,EAAE;CACf,MAAM,EAAE,OAAO,EAAE;CACjB,QAAQ,EAAE,SAAS,EAAE;CACrB,OAAO,EAAE,QAAQ,EAAE;CACnB,MAAM,EAAE,OAAO,EAAE;CACjB,MAAM,EAAE,OAAO,EAAE;AACnB;AAgBA,SAAgB,EAAQ,GAAkB,GAAoB,IAA2B,CAAC,GAA2B;CACnH,IAAM,IAAK,EAAc,GAAO,CAAO;CAEvC,OAAO,EAAY,GAAO,GAAM;EAAE;EAAI,cAAc,EAAQ;CAAa,CAAC,CAAC,CAAC,mBAAmB,CAAE;AACnG;AAWA,SAAgB,EAAM,GAAkB,GAAoB,IAA2B,CAAC,GAA2B;CACjH,IAAM,IAAK,EAAc,GAAO,CAAO;CAGvC,OAFqB,EAAY,GAAO,GAAM;EAAE;EAAI,cAAc,EAAQ;CAAa,CAEhF,CAAA,CAAa,mBAAmB,CAAE,CAAC,CAAC,IAAI,EAAc,EAAK,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC;AACjG"}
@@ -0,0 +1,2 @@
1
+ const e=require("./_tz.cjs"),t=require("./_convert.cjs");let n=require("@js-temporal/polyfill");var r=new WeakMap;function i(e){let t=r.get(e);if(t)return t;let n=Object.keys(e).map(t=>({key:t,ms:o(e[t])})).sort((e,t)=>e.ms-t.ms);return r.set(e,n),n}function a(e,r,a={},o=n.Temporal.Now.instant()){let s=t.toInstant(e,a).epochMilliseconds-o.epochMilliseconds;for(let{key:e,ms:t}of i(r))if(s<=t)return e;return null}function o(t){let r=n.Temporal.Duration.from(t);return(r.years??0)*12*e.MS_PER_MONTH+(r.months??0)*e.MS_PER_MONTH+(r.weeks??0)*7*864e5+(r.days??0)*864e5+(r.hours??0)*36e5+(r.minutes??0)*6e4+(r.seconds??0)*1e3+(r.milliseconds??0)+(r.microseconds??0)/1e3+(r.nanoseconds??0)/1e6}var s=[{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 c(e,t){return n.Temporal.ZonedDateTime.compare(e,t)<=0?t.since(e,{largestUnit:`year`}):e.since(t,{largestUnit:`year`})}function l(e){for(let{field:t,unit:n}of s){let r=Math.abs(e[t]);if(r>0)return{unit:n,value:r}}return{unit:`millisecond`,value:0}}function u(r,i,a={}){let o=i??n.Temporal.Now.instant();if(!a.tz&&r instanceof n.Temporal.Instant&&o instanceof n.Temporal.Instant)return l(c(r.toZonedDateTimeISO(`UTC`),o.toZonedDateTimeISO(`UTC`)));let s=e.inferSharedTimeZone([r,o],a);return l(c(t.toZoned(r,{tz:s}),t.toZoned(o,{tz:s})))}exports.expires=a,exports.timeDiff=u;
2
+ //# sourceMappingURL=classify.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.cjs","names":[],"sources":["../src/classify.ts"],"sourcesContent":["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"],"mappings":"gGAWA,IAAM,EAAuB,IAAI,QAEjC,SAAS,EAAsC,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,EAAa,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,EAAA,UAAU,EAAM,CAAO,CAAC,CAAC,kBAC1B,EAAI,kBAKlB,IAAK,GAAM,CAAE,MAAK,QAAQ,EAAoB,CAAU,EACtD,GAAI,GAAU,EAAI,OAAO,EAG3B,OAAO,IACT,CAGA,SAAS,EAAa,EAAyC,CAC7D,IAAM,EAAI,EAAA,SAAS,SAAS,KAAK,CAAQ,EAGzC,OACG,EAAE,OAAS,GAAK,GAAK,EAAA,cACrB,EAAE,QAAU,GAAK,EAAA,cACjB,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,EAAoF,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,EAAY,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,EAAS,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,EAAA,oBAAoB,CAAC,EAAG,CAAG,EAAG,CAAO,EAEhD,OAAO,EAAgB,EAAW,EAAA,QAAQ,EAAG,CAAE,IAAG,CAAC,EAAG,EAAA,QAAQ,EAAK,CAAE,IAAG,CAAC,CAAC,CAAC,CAC7E"}
@@ -0,0 +1,46 @@
1
+ import { Temporal } from '@js-temporal/polyfill';
2
+ import type { TimeDiffResult, TimeInput, TimeOptions } from './types';
3
+ /**
4
+ * Classifies a date into a user-defined bucket by comparing diff = date − now
5
+ * against the provided thresholds (sorted ascending). Returns the key of the
6
+ * first threshold the diff falls within, or `null` if no threshold matches.
7
+ *
8
+ * Thresholds accept negative durations to classify past dates. The function
9
+ * requires `options.tz` when input is a `PlainDate` or `PlainDateTime`.
10
+ *
11
+ * **Performance:** threshold objects are cached by reference in a `WeakMap`. Define
12
+ * the threshold record at module scope (not inline) so sorting is performed only once
13
+ * per unique object.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * expires(expiresAt, {
18
+ * longExpired: { days: -30 }, // more than 30 days in the past
19
+ * expired: { days: 0 }, // any past date
20
+ * critical: { days: 3 }, // within 3 days
21
+ * warning: { days: 14 }, // within 14 days
22
+ * safe: { years: 100 }, // catch-all for far future
23
+ * })
24
+ * // → 'longExpired' | 'expired' | 'critical' | 'warning' | 'safe' | null
25
+ * ```
26
+ */
27
+ export declare function expires<K extends string>(date: TimeInput, thresholds: Record<K, Temporal.DurationLike>, options?: TimeOptions, now?: Temporal.Instant): K | null;
28
+ /**
29
+ * Returns the absolute calendar-accurate difference between two dates as a
30
+ * structured `{ unit, value }` in the largest meaningful unit.
31
+ *
32
+ * When `b` is omitted, the current instant is used.
33
+ * Requires `options.tz` when inputs are `PlainDate`, `PlainDateTime`, or plain `Instant` with
34
+ * calendar-unit precision. Throws when timezone cannot be inferred from inputs.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * timeDiff(
39
+ * parseInstant('2026-01-01T00:00:00Z'),
40
+ * parseInstant('2027-03-15T00:00:00Z'),
41
+ * )
42
+ * // { unit: 'year', value: 1 }
43
+ * ```
44
+ */
45
+ export declare function timeDiff(a: TimeInput, b?: TimeInput, options?: TimeOptions): TimeDiffResult;
46
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../src/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAgB,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA2BpF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EACtC,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC5C,OAAO,GAAE,WAAgB,EACzB,GAAG,mBAAyB,GAC3B,CAAC,GAAG,IAAI,CAYV;AAkDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB,GAAG,cAAc,CAa/F"}
@@ -0,0 +1,83 @@
1
+ import { MS_PER_MONTH as e, inferSharedTimeZone as t } from "./_tz.js";
2
+ import { toInstant as n, toZoned as r } from "./_convert.js";
3
+ import { Temporal as i } from "@js-temporal/polyfill";
4
+ //#region src/classify.ts
5
+ var a = /* @__PURE__ */ new WeakMap();
6
+ function o(e) {
7
+ let t = a.get(e);
8
+ if (t) return t;
9
+ let n = Object.keys(e).map((t) => ({
10
+ key: t,
11
+ ms: c(e[t])
12
+ })).sort((e, t) => e.ms - t.ms);
13
+ return a.set(e, n), n;
14
+ }
15
+ function s(e, t, r = {}, a = i.Now.instant()) {
16
+ let s = n(e, r).epochMilliseconds - a.epochMilliseconds;
17
+ for (let { key: e, ms: n } of o(t)) if (s <= n) return e;
18
+ return null;
19
+ }
20
+ function c(t) {
21
+ let n = i.Duration.from(t);
22
+ return (n.years ?? 0) * 12 * e + (n.months ?? 0) * e + (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;
23
+ }
24
+ var l = [
25
+ {
26
+ field: "years",
27
+ unit: "year"
28
+ },
29
+ {
30
+ field: "months",
31
+ unit: "month"
32
+ },
33
+ {
34
+ field: "weeks",
35
+ unit: "week"
36
+ },
37
+ {
38
+ field: "days",
39
+ unit: "day"
40
+ },
41
+ {
42
+ field: "hours",
43
+ unit: "hour"
44
+ },
45
+ {
46
+ field: "minutes",
47
+ unit: "minute"
48
+ },
49
+ {
50
+ field: "seconds",
51
+ unit: "second"
52
+ },
53
+ {
54
+ field: "milliseconds",
55
+ unit: "millisecond"
56
+ }
57
+ ];
58
+ function u(e, t) {
59
+ return i.ZonedDateTime.compare(e, t) <= 0 ? t.since(e, { largestUnit: "year" }) : e.since(t, { largestUnit: "year" });
60
+ }
61
+ function d(e) {
62
+ for (let { field: t, unit: n } of l) {
63
+ let r = Math.abs(e[t]);
64
+ if (r > 0) return {
65
+ unit: n,
66
+ value: r
67
+ };
68
+ }
69
+ return {
70
+ unit: "millisecond",
71
+ value: 0
72
+ };
73
+ }
74
+ function f(e, n, a = {}) {
75
+ let o = n ?? i.Now.instant();
76
+ if (!a.tz && e instanceof i.Instant && o instanceof i.Instant) return d(u(e.toZonedDateTimeISO("UTC"), o.toZonedDateTimeISO("UTC")));
77
+ let s = t([e, o], a);
78
+ return d(u(r(e, { tz: s }), r(o, { tz: s })));
79
+ }
80
+ //#endregion
81
+ export { s as expires, f as timeDiff };
82
+
83
+ //# sourceMappingURL=classify.js.map