temporal-fmt 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +480 -9
  2. package/dist/calendarUtils.cjs +259 -4
  3. package/dist/calendarUtils.cjs.map +1 -1
  4. package/dist/calendarUtils.js +33 -1
  5. package/dist/chunk-4N3WCGEY.js +183 -0
  6. package/dist/{chunk-4JWGUR4O.js.map → chunk-4N3WCGEY.js.map} +1 -1
  7. package/dist/chunk-5UTV5VP6.js +984 -0
  8. package/dist/chunk-5UTV5VP6.js.map +1 -0
  9. package/dist/chunk-ACKKWH5Z.js +578 -0
  10. package/dist/{chunk-2JHEV7ZF.js.map → chunk-ACKKWH5Z.js.map} +1 -1
  11. package/dist/chunk-EGUUO6LE.js +439 -0
  12. package/dist/chunk-EGUUO6LE.js.map +1 -0
  13. package/dist/chunk-FBPHNRVW.js +2939 -0
  14. package/dist/chunk-FBPHNRVW.js.map +1 -0
  15. package/dist/chunk-GPB4JHGI.js +65 -0
  16. package/dist/chunk-GPB4JHGI.js.map +1 -0
  17. package/dist/chunk-H7YQX25T.js +170 -0
  18. package/dist/{chunk-J2AQNXF2.js.map → chunk-H7YQX25T.js.map} +1 -1
  19. package/dist/chunk-HXY2GUUM.js +182 -0
  20. package/dist/{chunk-EHLRZULM.js.map → chunk-HXY2GUUM.js.map} +1 -1
  21. package/dist/chunk-L24NPTAJ.js +328 -0
  22. package/dist/chunk-L24NPTAJ.js.map +1 -0
  23. package/dist/chunk-M3GDTHMT.js +169 -0
  24. package/dist/chunk-M3GDTHMT.js.map +1 -0
  25. package/dist/chunk-MKPDLQKD.js +51 -0
  26. package/dist/chunk-MKPDLQKD.js.map +1 -0
  27. package/dist/chunk-TSLPIURC.js +242 -0
  28. package/dist/{chunk-VQXUMFB2.js.map → chunk-TSLPIURC.js.map} +1 -1
  29. package/dist/chunk-UOPAV3WX.js +75 -0
  30. package/dist/chunk-UOPAV3WX.js.map +1 -0
  31. package/dist/chunk-VNIUIQA4.js +398 -0
  32. package/dist/chunk-VNIUIQA4.js.map +1 -0
  33. package/dist/chunk-YEUASGDK.js +9 -0
  34. package/dist/chunk-YEUASGDK.js.map +1 -0
  35. package/dist/chunk-ZQZF5EXW.js +29 -0
  36. package/dist/chunk-ZQZF5EXW.js.map +1 -0
  37. package/dist/config.d.cts +4 -2
  38. package/dist/config.d.ts +4 -2
  39. package/dist/duration.cjs +638 -26
  40. package/dist/duration.cjs.map +1 -1
  41. package/dist/duration.js +28 -1
  42. package/dist/format.cjs +833 -35
  43. package/dist/format.cjs.map +1 -1
  44. package/dist/format.js +20 -1
  45. package/dist/index.cjs +7698 -163
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +108 -24
  48. package/dist/index.d.ts +108 -24
  49. package/dist/index.js +1536 -58
  50. package/dist/index.js.map +1 -1
  51. package/dist/interval.cjs +5480 -35
  52. package/dist/interval.cjs.map +1 -1
  53. package/dist/interval.js +46 -1
  54. package/dist/localeRegistry.cjs +375 -32
  55. package/dist/localeRegistry.cjs.map +1 -1
  56. package/dist/localeRegistry.js +11 -1
  57. package/dist/modApi.d.cts +131 -0
  58. package/dist/modApi.d.ts +131 -0
  59. package/dist/parse.cjs +1797 -70
  60. package/dist/parse.cjs.map +1 -1
  61. package/dist/parse.js +20 -1
  62. package/dist/pattern.d.cts +1 -0
  63. package/dist/pattern.d.ts +1 -0
  64. package/dist/recurrence.cjs +452 -20
  65. package/dist/recurrence.cjs.map +1 -1
  66. package/dist/recurrence.js +19 -1
  67. package/dist/relativeTime.cjs +268 -21
  68. package/dist/relativeTime.cjs.map +1 -1
  69. package/dist/relativeTime.js +14 -1
  70. package/dist/runtime.d.cts +201 -0
  71. package/dist/runtime.d.ts +201 -0
  72. package/dist/timezone.cjs +387 -20
  73. package/dist/timezone.cjs.map +1 -1
  74. package/dist/timezone.js +26 -1
  75. package/package.json +4 -6
  76. package/scripts/cli.mjs +27 -5
  77. package/dist/chunk-2JHEV7ZF.js +0 -7
  78. package/dist/chunk-3MZLTVP3.js +0 -2
  79. package/dist/chunk-3MZLTVP3.js.map +0 -1
  80. package/dist/chunk-4JWGUR4O.js +0 -11
  81. package/dist/chunk-5U5WJ465.js +0 -36
  82. package/dist/chunk-5U5WJ465.js.map +0 -1
  83. package/dist/chunk-C5YESWFT.js +0 -2
  84. package/dist/chunk-C5YESWFT.js.map +0 -1
  85. package/dist/chunk-C6ZZ62ET.js +0 -2
  86. package/dist/chunk-C6ZZ62ET.js.map +0 -1
  87. package/dist/chunk-EHLRZULM.js +0 -5
  88. package/dist/chunk-F4RGUDA3.js +0 -2
  89. package/dist/chunk-F4RGUDA3.js.map +0 -1
  90. package/dist/chunk-J2AQNXF2.js +0 -2
  91. package/dist/chunk-NBXF7V5B.js +0 -2
  92. package/dist/chunk-NBXF7V5B.js.map +0 -1
  93. package/dist/chunk-R52YOKI3.js +0 -7
  94. package/dist/chunk-R52YOKI3.js.map +0 -1
  95. package/dist/chunk-UJR6DHXY.js +0 -2
  96. package/dist/chunk-UJR6DHXY.js.map +0 -1
  97. package/dist/chunk-VQXUMFB2.js +0 -21
  98. package/dist/chunk-XH46JNPK.js +0 -2
  99. package/dist/chunk-XH46JNPK.js.map +0 -1
  100. package/dist/chunk-YB6YAG7E.js +0 -35
  101. package/dist/chunk-YB6YAG7E.js.map +0 -1
  102. package/dist/chunk-ZGMTY52O.js +0 -2
  103. package/dist/chunk-ZGMTY52O.js.map +0 -1
package/dist/format.cjs CHANGED
@@ -1,39 +1,837 @@
1
- "use strict";var V=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var te=(e,t)=>{for(var n in t)V(e,n,{get:t[n],enumerable:!0})},ne=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of J(t))!ee.call(e,i)&&i!==n&&V(e,i,{get:()=>t[i],enumerable:!(o=Q(t,i))||o.enumerable});return e};var oe=e=>ne(V({},"__esModule",{value:!0}),e);var Le={};te(Le,{_getPieces:()=>Ee,_handlerFor:()=>be,compileFormat:()=>ke,format:()=>we,formatToParts:()=>Te});module.exports=oe(Le);var re,ae=[];function W(e){ae.push(e)}function ie(){return re??globalThis.Temporal}function K(){let e=ie();if(!e)throw new Error("temporal-fmt: parse() needs a Temporal implementation to construct its result. Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal (native on Node 26+, or a polyfill like temporal-polyfill).");return e}var w=class extends Error{code;input;format;token;position;expected;actual;reason;constructor(t,n){super(t),this.name="TemporalFmtError",this.code=n.code,this.input=n.input,this.format=n.format,this.token=n.token,this.position=n.position,this.expected=n.expected,this.actual=n.actual,this.reason=n.reason;let o=Error.captureStackTrace;typeof o=="function"&&o(this,this.constructor)}toJSON(){return{name:this.name,message:this.message,code:this.code,input:this.input,format:this.format,token:this.token,position:this.position,expected:this.expected,actual:this.actual,reason:this.reason}}},c=class extends w{constructor(t){let{message:n,...o}=t;super(n??`format string "${t.format??""}" has a syntax error${t.reason?`: ${t.reason}`:""}.`,{code:"FORMAT_SYNTAX_ERROR",...o}),this.name="FormatSyntaxError"}},d=class extends w{constructor(t){let{message:n,...o}=t;super(n??`token "${t.token??""}" is not a recognized temporal-fmt token${t.format?` in format string "${t.format}"`:""}.`,{code:"UNKNOWN_TOKEN",...o}),this.name="UnknownTokenError"}};/* c8 ignore start @preserve -- InvalidTimeError is part of the public
2
- error-class surface (exported from index.ts, code 'INVALID_TIME')
3
- but nothing in this package constructs one. Investigated wiring it
4
- in the same way InvalidTimeZoneError was just wired (see parse.ts's
5
- zzz-validation loop): checked whether hour/minute/second have a
6
- post-match semantic range check the way zone ids do. They don't —
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/format.ts
21
+ var format_exports = {};
22
+ __export(format_exports, {
23
+ _getPieces: () => _getPieces,
24
+ _handlerFor: () => _handlerFor,
25
+ compileFormat: () => compileFormat,
26
+ format: () => format,
27
+ formatToParts: () => formatToParts
28
+ });
29
+ module.exports = __toCommonJS(format_exports);
30
+
31
+ // src/temporalProvider.ts
32
+ var injectedTemporal;
33
+ var onTemporalChanged = [];
34
+ function subscribeToTemporalChanges(listener) {
35
+ onTemporalChanged.push(listener);
36
+ }
37
+ function resolveTemporal() {
38
+ return injectedTemporal ?? globalThis.Temporal;
39
+ }
40
+ function getTemporal() {
41
+ const temporal = resolveTemporal();
42
+ if (!temporal) {
43
+ throw new Error(
44
+ "temporal-fmt: parse() needs a Temporal implementation to construct its result. Call setTemporal(Temporal) once at startup, or assign one to globalThis.Temporal (native on Node 26+, or a polyfill like temporal-polyfill)."
45
+ );
46
+ }
47
+ return temporal;
48
+ }
49
+
50
+ // src/errors.ts
51
+ var TemporalFmtError = class extends Error {
52
+ code;
53
+ input;
54
+ format;
55
+ token;
56
+ position;
57
+ expected;
58
+ actual;
59
+ reason;
60
+ constructor(message, fields) {
61
+ super(message);
62
+ this.name = "TemporalFmtError";
63
+ this.code = fields.code;
64
+ this.input = fields.input;
65
+ this.format = fields.format;
66
+ this.token = fields.token;
67
+ this.position = fields.position;
68
+ this.expected = fields.expected;
69
+ this.actual = fields.actual;
70
+ this.reason = fields.reason;
71
+ const capture = Error.captureStackTrace;
72
+ if (typeof capture === "function") {
73
+ capture(this, this.constructor);
74
+ }
75
+ }
76
+ // lets callers just call err.toJSON() for logging. plain Error doesn't
77
+ // serialize its non-enumerable fields on its own, so this picks them
78
+ // up explicitly
79
+ toJSON() {
80
+ return {
81
+ name: this.name,
82
+ message: this.message,
83
+ code: this.code,
84
+ input: this.input,
85
+ format: this.format,
86
+ token: this.token,
87
+ position: this.position,
88
+ expected: this.expected,
89
+ actual: this.actual,
90
+ reason: this.reason
91
+ };
92
+ }
93
+ };
94
+ var FormatSyntaxError = class extends TemporalFmtError {
95
+ constructor(fields) {
96
+ const { message, ...rest } = fields;
97
+ super(
98
+ message ?? `format string "${fields.format ?? ""}" has a syntax error${fields.reason ? `: ${fields.reason}` : ""}.`,
99
+ { code: "FORMAT_SYNTAX_ERROR", ...rest }
100
+ );
101
+ this.name = "FormatSyntaxError";
102
+ }
103
+ };
104
+ var UnknownTokenError = class extends TemporalFmtError {
105
+ constructor(fields) {
106
+ const { message, ...rest } = fields;
107
+ super(
108
+ message ?? `token "${fields.token ?? ""}" is not a recognized temporal-fmt token${fields.format ? ` in format string "${fields.format}"` : ""}.`,
109
+ { code: "UNKNOWN_TOKEN", ...rest }
110
+ );
111
+ this.name = "UnknownTokenError";
112
+ }
113
+ };
114
+ /* c8 ignore start @preserve -- InvalidTimeError is part of the public
115
+ error-class surface (exported from index.ts, code 'INVALID_TIME') but
116
+ nothing in this package actually constructs one. went and checked
117
+ whether it could get wired in the same way InvalidTimeZoneError just
118
+ was (see parse.ts's zzz-validation loop) — does hour/minute/second have
119
+ a post-match semantic range check the way zone ids do? nope.
7
120
  pattern.ts's regex fragments for HH/H/hh/h/mm/m/ss/s already enforce
8
- their valid ranges at the regex level (e.g. HH is '(?:[01]\d|2[0-3])',
9
- which can't match "99" in the first place), so an out-of-range time
10
- is rejected as a plain shape mismatch before any semantic check
11
- could run. There's no live gap to hook this into without inventing a
12
- redundant check purely to give this class a body. Left unconstructed
13
- until the library actually has a real invalid-time case to report. *//* c8 ignore stop @preserve *//* c8 ignore start @preserve -- InvalidCalendarError is part of the
121
+ their valid ranges right at the regex level (e.g. HH is
122
+ '(?:[01]\d|2[0-3])', which literally can't match "99"), so an
123
+ out-of-range time just gets rejected as a plain shape mismatch before
124
+ any semantic check would even run. no live gap to hook this into
125
+ without inventing a redundant check purely to give this class a body.
126
+ leaving it unconstructed until the library actually hits a real
127
+ invalid-time case worth reporting */
128
+ /* c8 ignore stop @preserve */
129
+ /* c8 ignore start @preserve -- InvalidCalendarError is part of the
14
130
  public error-class surface (exported from index.ts, code
15
- 'INVALID_CALENDAR') but nothing in this package constructs one.
16
- Checked for a wiring opportunity the same way InvalidTimeZoneError
17
- got one: there's no user-supplied calendar identifier anywhere in
18
- the library to validate against a supported list. resolveCalendar()
131
+ 'INVALID_CALENDAR') but same story as InvalidTimeError above — nothing
132
+ in this package constructs one. checked for a wiring opportunity the
133
+ same way: there's no user-supplied calendar identifier anywhere in the
134
+ library that we'd validate against a supported list. resolveCalendar()
19
135
  in parse.ts derives the calendar entirely from Intl's own resolution
20
- of the locale string — it's never handed an arbitrary "calendar"
21
- value a caller could get wrong. There's no live input to reject.
22
- Left unconstructed until the library actually accepts a calendar
23
- parameter that could be invalid. *//* c8 ignore stop @preserve */var g=class extends w{constructor(t){let{message:n,...o}=t;super(n??`locale "${t.actual??""}" is not a valid BCP-47 tag${t.reason?`: ${t.reason}`:""}.`,{code:"INVALID_LOCALE",...o}),this.name="InvalidLocaleError"}};/* c8 ignore start @preserve -- unreachable from the current test suite,
24
- see rationale above *//* c8 ignore stop @preserve */var se=new Map;function R(e){return e.replace(/_/g,"-")}var T=new Map,me=500;function X(e){let t=T.get(e);if(t!==void 0)return t;let n;try{n=new Intl.Locale(e.replace(/_/g,"-")).toString().toLowerCase()}catch{n=e}if(T.size>=me){let o=T.keys().next().value;o!==void 0&&T.delete(o)}return T.set(e,n),n}function u(e){let t=X(e);return se.get(t)}var ce=[0,31,59,90,120,151,181,212,243,273,304,334];function Y(e){return e%4===0&&e%100!==0||e%400===0}function k(e){return Y(e)?366:365}function E(e,t,n){let o=ce[t-1]+n;return t>2&&Y(e)&&(o+=1),o}var U=2e3;function le(e){let t=0;if(e>=U)for(let o=U;o<e;o++)t+=k(o);else for(let o=e;o<U;o++)t-=k(o);return((5+t)%7+7)%7+1}function Z(e,t,n,o){let a=E(e,t,n)+(4-o),r,s;a<1?(r=e-1,s=a+k(r)):a>k(e)?(r=e+1,s=a-k(e)):(r=e,s=a);let I=1+(4-le(r)+7)%7,M=1+Math.floor((s-I)/7);return{isoYear:r,week:M}}function m(e,t){let n=e<0,o=String(Math.abs(e)).padStart(t,"0");return n?"-"+o:o}m.fraction=function(t,n){let o=t.millisecond*1e6+(t.microsecond??0)*1e3+(t.nanosecond??0);return m(o,9).slice(0,n)};var S="en-US",b=new Map,ue=500;function j(e,t){let n=JSON.stringify([X(e),t]),o=b.get(n);if(o)return o;if(b.size>=ue){let i=b.keys().next().value;i!==void 0&&b.delete(i)}try{o=new Intl.DateTimeFormat(R(e),t)}catch(i){throw new g({actual:e,reason:i.message})}return b.set(n,o),o}var L;W(()=>{L=void 0});function de(){if(L===void 0){L=!1;try{let e=K();new Intl.DateTimeFormat("en-US",{day:"numeric"}).formatToParts(e.PlainDate.from({year:1970,month:1,day:1})),L=!0}catch{}}return L}function F(e,t,n,o){let i=e?.calendarId,a={...n,calendar:i&&i!=="iso8601"?i:"gregory"},r=e.toLocaleString;if(typeof r!="function"||r===Object.prototype.toLocaleString)throw new Error(`temporal-fmt: locale-aware part "${o}" needs a value that implements toLocaleString (a real Temporal object). A plain field bag cannot render locale-aware names \u2014 pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`);if(!de())try{return r.call(e,R(t),a)}catch(y){throw y instanceof RangeError?new g({actual:t,reason:y.message}):y}let{toInstant:s,timeZoneId:l}=e,I=typeof s=="function"&&typeof l=="string",M=I?e.toInstant():e,B={...a,...I?{timeZone:l}:{}},v=j(t,B).formatToParts(M),N=v.findIndex(y=>y.type===o);if(N===-1)throw new Error(`temporal-fmt: locale "${t}" produced no "${o}" part for this token. This usually means the Temporal object is missing the field the token needs.`);let h=v[N].value,_=v[N-1],C=v[N+1];return _?.type==="literal"&&!/\s/.test(_.value)&&(h=_.value+h),C?.type==="literal"&&!/\s/.test(C.value)&&(h=h+C.value),h}function pe(e,t){let n=u(t);if(n)return e<12?n.dayPeriod[0]:n.dayPeriod[1];let o=new Date(Date.UTC(1970,0,1,e)),a=j(t,{hour:"numeric",hour12:!0,timeZone:"UTC"}).formatToParts(o).find(r=>r.type==="dayPeriod");if(!a)throw new Error(`temporal-fmt: locale "${t}" produced no "dayPeriod" part for token "a".`);return a.value}function p(e,t,n,o,i,a){return i&&a!==void 0&&a>=0&&a<i.length?i[a]:F(e,t,n,o)}function f(e,t){if(e==="+00:00"&&(t==="X"||t==="XX"||t==="XXX"))return"Z";let n=e[0],o=e.slice(1,3),i=e.slice(4,6);switch(t){case"X":case"x":return i==="00"?`${n}${o}`:`${n}${o}${i}`;case"XX":case"xx":return`${n}${o}${i}`;case"XXX":case"xxx":return`${n}${o}:${i}`}}var A=[["yyyy",e=>m(e.year,4),"year"],["yy",e=>{if(e.year<0)throw new Error(`temporal-fmt: token "yy" doesn't support negative years (got ${e.year}), since truncating to 2 digits would make it indistinguishable from a positive year. Use "yyyy" instead.`);return m(e.year%100,2)},"year"],["MMMM",(e,t)=>{let n=u(t);return p(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["MMM",(e,t)=>{let n=u(t);return p(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["MM",e=>m(e.month,2),"month"],["M",e=>String(e.month),"month"],["dd",e=>m(e.day,2),"day"],["d",e=>String(e.day),"day"],["EEEE",(e,t)=>{let n=u(t);return p(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["EEE",(e,t)=>{let n=u(t);return p(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["HH",e=>m(e.hour,2),"hour"],["H",e=>String(e.hour),"hour"],["hh",e=>m(e.hour%12||12,2),"hour"],["h",e=>String(e.hour%12||12),"hour"],["mm",e=>m(e.minute,2),"minute"],["m",e=>String(e.minute),"minute"],["ss",e=>m(e.second,2),"second"],["s",e=>String(e.second),"second"],["SSSSSSSSS",e=>m.fraction(e,9),"millisecond"],["SSSSSSSS",e=>m.fraction(e,8),"millisecond"],["SSSSSSS",e=>m.fraction(e,7),"millisecond"],["SSSSSS",e=>m.fraction(e,6),"millisecond"],["SSSSS",e=>m.fraction(e,5),"millisecond"],["SSSS",e=>m.fraction(e,4),"millisecond"],["SSS",e=>m.fraction(e,3),"millisecond"],["SS",e=>m.fraction(e,2),"millisecond"],["S",e=>m.fraction(e,1),"millisecond"],["a",(e,t)=>pe(e.hour,t),"hour"],["zzz",e=>e.timeZoneId,"timeZoneId"],["xxx",e=>f(e.offset,"xxx"),"offset"],["xx",e=>f(e.offset,"xx"),"offset"],["X",e=>f(e.offset,"X"),"offset"],["XX",e=>f(e.offset,"XX"),"offset"],["XXX",e=>f(e.offset,"XXX"),"offset"],["x",e=>f(e.offset,"x"),"offset"],["do",e=>{let t=e.day,n=t%10,o=t%100;return o>=11&&o<=13?t+"th":n===1?t+"st":n===2?t+"nd":n===3?t+"rd":t+"th"},"day"],["Q",e=>String(Math.ceil(e.month/3)),"month"],["QQQ",e=>"Q"+Math.ceil(e.month/3),"month"],["ww",e=>{let{week:t}=Z(e.year,e.month,e.day,e.dayOfWeek);return m(t,2)},"dayOfWeek"],["RRRR",e=>{let{isoYear:t}=Z(e.year,e.month,e.day,e.dayOfWeek);return m(t,4)},"dayOfWeek"],["D",e=>String(E(e.year,e.month,e.day)),"day"],["DD",e=>m(E(e.year,e.month,e.day),2),"day"],["DDD",e=>m(E(e.year,e.month,e.day),3),"day"],["LLLL",(e,t)=>{let n=u(t);return p(e,t,{month:"long"},"month",n?.monthLong,e.month-1)},"month"],["LLL",(e,t)=>{let n=u(t);return p(e,t,{month:"short"},"month",n?.monthShort,e.month-1)},"month"],["cccc",(e,t)=>{let n=u(t);return p(e,t,{weekday:"long"},"weekday",n?.weekdayLong,e.dayOfWeek-1)},"dayOfWeek"],["ccc",(e,t)=>{let n=u(t);return p(e,t,{weekday:"short"},"weekday",n?.weekdayShort,e.dayOfWeek-1)},"dayOfWeek"],["GGGG",(e,t)=>F(e,t,{era:"long"},"era"),"year"],["G",(e,t)=>F(e,t,{era:"short"},"era"),"year"],["zzzz",(e,t)=>F(e,t,{timeZoneName:"longGeneric"},"timeZoneName"),"timeZoneId"],["z",(e,t)=>F(e,t,{timeZoneName:"short"},"timeZoneName"),"timeZoneId"]];var ge=A.map(([e])=>e).sort((e,t)=>t.length-e.length);function H(e){let t=[],n=0;for(;n<e.length;){let o=e[n];if(o==="'"){if(e[n+1]==="'"){z(t,"'"),n+=2;continue}let a=n+1,r="",s=!1;for(;a<e.length;){if(e[a]==="'"){if(e[a+1]==="'"){r+="'",a+=2;continue}s=!0,a+=1;break}r+=e[a],a+=1}if(!s)throw new c({format:e,message:`temporal-fmt: unterminated quote in format string "${e}"`});z(t,r),n=a;continue}let i=ge.find(a=>e.startsWith(a,n));if(i){let a=i[i.length-1];if(e[n+i.length]===a){let r=n+i.length;for(;e[r]===a;)r+=1;throw new d({format:e,token:e.slice(n,r),message:`temporal-fmt: "${e.slice(n,r)}" in format string "${e}" isn't a recognized token \u2014 did you mean "${i}"?`})}t.push({kind:"token",value:i}),n+=i.length;continue}z(t,o),n+=1}return t}function z(e,t){let n=e[e.length-1];n&&n.kind==="literal"?n.value+=t:e.push({kind:"literal",value:t})}var G=new Set(["latn","arab","deva","beng","guru","gujr","orya","tamldec","telu","knda","mlym","fullwide","hanidec"]),q=new Map;function fe(e){let t=q.get(e);if(t)return t;/* c8 ignore start @preserve -- dead by construction, not just
25
- untested: both callers (convertDigits, convertDigitsToAscii) already
26
- return early on system === 'latn' before calling getDigitMap at all,
27
- so this function is never invoked with 'latn'. Kept as a defensive
28
- fallback rather than trusting that stays true for any future
29
- caller. */if(e==="latn"){t={};for(let n=0;n<10;n++)t[String(n)]=String(n)}else{/* c8 ignore stop @preserve */let n=new Intl.NumberFormat("en-US-u-nu-"+e,{useGrouping:!1});t={};for(let o=0;o<10;o++)t[String(o)]=n.format(o)}return q.set(e,t),t}function he(e,t){if(t==="latn")return e;if(!G.has(t))throw new g({actual:t,reason:`numbering system "${t}" is not supported. Supported: ${[...G].join(", ")}.`});let n=fe(t),o="";for(let i of e)i>="0"&&i<="9"?o+=n[i]??i:o+=i;return o}function x(e,t){let n=t.numberingSystem??"latn";return n==="latn"?e:he(e,n)}var O=new Map(A.map(([e,t,n])=>[e,{fn:t,field:n}])),$=new Map,ye=500;function P(e){let t=$.get(e);if(t)return t;if($.size>=ye){let n=$.keys().next().value;n!==void 0&&$.delete(n)}return t=H(e),$.set(e,t),t}function we(e,t,n={}){if(t.length>1e3)throw new c({format:t,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${t.length}).`});let o=n.locale??S,i=P(t),a="";for(let r of i){if(r.kind==="literal"){a+=r.value;continue}let s=O.get(r.value);/* c8 ignore start @preserve -- unreachable: tokenize() only ever emits
30
- tokens present in TOKENS, and HANDLER_BY_TOKEN is built from that
31
- same list, so every token piece.value can hold already has an entry
32
- here. Same defensive-guard category as the twin checks in
33
- analyze.ts/formatDuration.ts. */if(!s)throw new d({token:r.value,format:t,message:`temporal-fmt: unknown token "${r.value}"`});/* c8 ignore stop @preserve */if(e[s.field]===void 0)throw new c({format:t,token:r.value,message:`temporal-fmt: token "${r.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`});a+=s.fn(e,o)}return x(a,n)}function Te(e,t,n={}){if(t.length>1e3)throw new c({format:t,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${t.length}).`});let o=n.locale??S,i=P(t),a=[];for(let r of i){if(r.kind==="literal"){let l=a[a.length-1];/* c8 ignore start @preserve -- unreachable through tokenize()'s own
34
- output: appendLiteral() in tokenize.ts already merges every
35
- adjacent literal character before pieces ever reaches here, so
36
- two 'literal' pieces never sit back-to-back in the array this
37
- loop walks. Kept as defense-in-depth in case that invariant ever
38
- changes upstream. */if(l&&l.type==="literal")l.value+=r.value;else{/* c8 ignore stop @preserve */a.push({type:"literal",value:r.value})}continue}let s=O.get(r.value);/* c8 ignore start @preserve -- unreachable, see the note in format() above */if(!s)throw new d({token:r.value,format:t,message:`temporal-fmt: unknown token "${r.value}"`});/* c8 ignore stop @preserve */if(e[s.field]===void 0)throw new c({format:t,token:r.value,message:`temporal-fmt: token "${r.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`});a.push({type:"token",value:x(s.fn(e,o),n),token:r.value})}return a}function ke(e){if(e.length>1e3)throw new c({format:e,message:`temporal-fmt: format string exceeds maximum length of ${1e3} characters (got ${e.length}).`});let t=P(e);return{formatStr:e,pieces:t,format(n,o={}){let i=o.locale??S,a="";for(let r of t){if(r.kind==="literal"){a+=r.value;continue}let s=O.get(r.value);if(!s)throw new d({token:r.value,format:e,message:`temporal-fmt: unknown token "${r.value}"`});if(n[s.field]===void 0)throw new c({format:e,token:r.value,message:`temporal-fmt: token "${r.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`});a+=s.fn(n,i)}return x(a,o)},formatToParts(n,o={}){let i=o.locale??S,a=[];for(let r of t){if(r.kind==="literal"){let l=a[a.length-1];l&&l.type==="literal"?l.value+=r.value:a.push({type:"literal",value:r.value});continue}let s=O.get(r.value);if(!s)throw new d({token:r.value,format:e,message:`temporal-fmt: unknown token "${r.value}"`});if(n[s.field]===void 0)throw new c({format:e,token:r.value,message:`temporal-fmt: token "${r.value}" requires "${s.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`});a.push({type:"token",value:x(s.fn(n,i),o),token:r.value})}return a}}}function Ee(e){return P(e)}function be(e){return O.get(e)}0&&(module.exports={_getPieces,_handlerFor,compileFormat,format,formatToParts});
136
+ of the locale string — it's never handed an arbitrary "calendar" value
137
+ a caller could actually get wrong. no live input to reject here.
138
+ leaving it unconstructed until the library accepts an actual calendar
139
+ parameter that could be invalid */
140
+ /* c8 ignore stop @preserve */
141
+ var InvalidLocaleError = class extends TemporalFmtError {
142
+ constructor(fields) {
143
+ const { message, ...rest } = fields;
144
+ super(
145
+ message ?? `locale "${fields.actual ?? ""}" is not a valid BCP-47 tag${fields.reason ? `: ${fields.reason}` : ""}.`,
146
+ { code: "INVALID_LOCALE", ...rest }
147
+ );
148
+ this.name = "InvalidLocaleError";
149
+ }
150
+ };
151
+ /* c8 ignore start @preserve -- unreachable from the current test suite,
152
+ see rationale above */
153
+ /* c8 ignore stop @preserve */
154
+
155
+ // src/localeVocab.ts
156
+ var customVocabs = /* @__PURE__ */ new Map();
157
+ function normalizeLocaleTag(locale) {
158
+ return locale.replace(/_/g, "-");
159
+ }
160
+ var canonicalKeyCache = /* @__PURE__ */ new Map();
161
+ var MAX_CANONICAL_KEY_CACHE = 500;
162
+ function canonicalCacheKey(locale) {
163
+ const hit = canonicalKeyCache.get(locale);
164
+ if (hit !== void 0) return hit;
165
+ let key;
166
+ try {
167
+ key = new Intl.Locale(locale.replace(/_/g, "-")).toString().toLowerCase();
168
+ } catch {
169
+ key = locale;
170
+ }
171
+ if (canonicalKeyCache.size >= MAX_CANONICAL_KEY_CACHE) {
172
+ const oldestKey = canonicalKeyCache.keys().next().value;
173
+ if (oldestKey !== void 0) canonicalKeyCache.delete(oldestKey);
174
+ }
175
+ canonicalKeyCache.set(locale, key);
176
+ return key;
177
+ }
178
+ function getCustomVocab(locale) {
179
+ const cacheKey = canonicalCacheKey(locale);
180
+ return customVocabs.get(cacheKey);
181
+ }
182
+
183
+ // src/isoWeek.ts
184
+ var CUMULATIVE_DAYS_BY_MONTH = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
185
+ function isGregorianLeapYear(year) {
186
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
187
+ }
188
+ function daysInYear(year) {
189
+ return isGregorianLeapYear(year) ? 366 : 365;
190
+ }
191
+ function dayOfYear(year, month, day) {
192
+ let doy = CUMULATIVE_DAYS_BY_MONTH[month - 1] + day;
193
+ if (month > 2 && isGregorianLeapYear(year)) doy += 1;
194
+ return doy;
195
+ }
196
+ var REFERENCE_YEAR = 2e3;
197
+ function dayOfWeekOfJan1(year) {
198
+ let offset = 0;
199
+ if (year >= REFERENCE_YEAR) {
200
+ for (let y = REFERENCE_YEAR; y < year; y++) offset += daysInYear(y);
201
+ } else {
202
+ for (let y = year; y < REFERENCE_YEAR; y++) offset -= daysInYear(y);
203
+ }
204
+ const zeroIndexed = ((6 - 1 + offset) % 7 + 7) % 7;
205
+ return zeroIndexed + 1;
206
+ }
207
+ function isoWeekYearAndWeek(year, month, day, dayOfWeek) {
208
+ const doy = dayOfYear(year, month, day);
209
+ const thursdayDoyRelative = doy + (4 - dayOfWeek);
210
+ let isoYear;
211
+ let thursdayDoy;
212
+ if (thursdayDoyRelative < 1) {
213
+ isoYear = year - 1;
214
+ thursdayDoy = thursdayDoyRelative + daysInYear(isoYear);
215
+ } else if (thursdayDoyRelative > daysInYear(year)) {
216
+ isoYear = year + 1;
217
+ thursdayDoy = thursdayDoyRelative - daysInYear(year);
218
+ } else {
219
+ isoYear = year;
220
+ thursdayDoy = thursdayDoyRelative;
221
+ }
222
+ const jan1Dow = dayOfWeekOfJan1(isoYear);
223
+ const firstThursdayDoy = 1 + (4 - jan1Dow + 7) % 7;
224
+ const week = 1 + Math.floor((thursdayDoy - firstThursdayDoy) / 7);
225
+ return { isoYear, week };
226
+ }
227
+
228
+ // src/tokens.ts
229
+ function pad(n, len) {
230
+ const negative = n < 0;
231
+ const digits = String(Math.abs(n)).padStart(len, "0");
232
+ return negative ? "-" + digits : digits;
233
+ }
234
+ pad.fraction = function formatFraction(t, width) {
235
+ const nanoOfSecond = t.millisecond * 1e6 + (t.microsecond ?? 0) * 1e3 + (t.nanosecond ?? 0);
236
+ return pad(nanoOfSecond, 9).slice(0, width);
237
+ };
238
+ var DEFAULT_LOCALE = "en-US";
239
+ var formatterCache = /* @__PURE__ */ new Map();
240
+ var MAX_CACHE_SIZE = 500;
241
+ function getFormatter(locale, options) {
242
+ const key = JSON.stringify([canonicalCacheKey(locale), options]);
243
+ let formatter = formatterCache.get(key);
244
+ if (formatter) {
245
+ return formatter;
246
+ }
247
+ if (formatterCache.size >= MAX_CACHE_SIZE) {
248
+ const oldestKey = formatterCache.keys().next().value;
249
+ if (oldestKey !== void 0) formatterCache.delete(oldestKey);
250
+ }
251
+ try {
252
+ formatter = new Intl.DateTimeFormat(normalizeLocaleTag(locale), options);
253
+ } catch (err) {
254
+ throw new InvalidLocaleError({ actual: locale, reason: err.message });
255
+ }
256
+ formatterCache.set(key, formatter);
257
+ return formatter;
258
+ }
259
+ var nativeSupport;
260
+ subscribeToTemporalChanges(() => {
261
+ nativeSupport = void 0;
262
+ });
263
+ function intlSupportsNativeTemporal() {
264
+ if (nativeSupport === void 0) {
265
+ nativeSupport = false;
266
+ try {
267
+ const temporal = getTemporal();
268
+ new Intl.DateTimeFormat("en-US", { day: "numeric" }).formatToParts(temporal.PlainDate.from({ year: 1970, month: 1, day: 1 }));
269
+ nativeSupport = true;
270
+ } catch {
271
+ }
272
+ }
273
+ return nativeSupport;
274
+ }
275
+ function intlPart(temporal, locale, options, partType) {
276
+ const calendar = temporal?.calendarId;
277
+ const formatterOptions = {
278
+ ...options,
279
+ calendar: calendar && calendar !== "iso8601" ? calendar : "gregory"
280
+ };
281
+ const ls = temporal.toLocaleString;
282
+ if (typeof ls !== "function" || ls === Object.prototype.toLocaleString) {
283
+ throw new Error(
284
+ `temporal-fmt: locale-aware part "${partType}" needs a value that implements toLocaleString (a real Temporal object). A plain field bag cannot render locale-aware names \u2014 pass a Temporal.PlainDate/PlainDateTime/ZonedDateTime.`
285
+ );
286
+ }
287
+ if (!intlSupportsNativeTemporal()) {
288
+ try {
289
+ return ls.call(temporal, normalizeLocaleTag(locale), formatterOptions);
290
+ } catch (err) {
291
+ if (err instanceof RangeError) {
292
+ throw new InvalidLocaleError({ actual: locale, reason: err.message });
293
+ }
294
+ throw err;
295
+ }
296
+ }
297
+ const { toInstant, timeZoneId } = temporal;
298
+ const isZoned = typeof toInstant === "function" && typeof timeZoneId === "string";
299
+ const intlSafeTemporal = isZoned ? temporal.toInstant() : temporal;
300
+ const nativeOptions = {
301
+ ...formatterOptions,
302
+ ...isZoned ? { timeZone: timeZoneId } : {}
303
+ };
304
+ const formatter = getFormatter(locale, nativeOptions);
305
+ const parts = formatter.formatToParts(intlSafeTemporal);
306
+ const index = parts.findIndex((p) => p.type === partType);
307
+ if (index === -1) {
308
+ throw new Error(
309
+ `temporal-fmt: locale "${locale}" produced no "${partType}" part for this token. This usually means the Temporal object is missing the field the token needs.`
310
+ );
311
+ }
312
+ let value = parts[index].value;
313
+ const prev = parts[index - 1];
314
+ const next = parts[index + 1];
315
+ if (prev?.type === "literal" && !/\s/.test(prev.value)) value = prev.value + value;
316
+ if (next?.type === "literal" && !/\s/.test(next.value)) value = value + next.value;
317
+ return value;
318
+ }
319
+ function dayPeriodPart(hour, locale) {
320
+ const custom = getCustomVocab(locale);
321
+ if (custom) {
322
+ return hour < 12 ? custom.dayPeriod[0] : custom.dayPeriod[1];
323
+ }
324
+ const date = new Date(Date.UTC(1970, 0, 1, hour));
325
+ const formatter = getFormatter(locale, { hour: "numeric", hour12: true, timeZone: "UTC" });
326
+ const part = formatter.formatToParts(date).find((p) => p.type === "dayPeriod");
327
+ if (!part) {
328
+ throw new Error(`temporal-fmt: locale "${locale}" produced no "dayPeriod" part for token "a".`);
329
+ }
330
+ return part.value;
331
+ }
332
+ function localeAwareName(temporal, locale, options, partType, customArray, customIndex) {
333
+ if (customArray && customIndex !== void 0 && customIndex >= 0 && customIndex < customArray.length) {
334
+ return customArray[customIndex];
335
+ }
336
+ return intlPart(temporal, locale, options, partType);
337
+ }
338
+ function formatOffset(offset, variant) {
339
+ if (offset === "+00:00" && (variant === "X" || variant === "XX" || variant === "XXX")) {
340
+ return "Z";
341
+ }
342
+ if (offset.length > 6) {
343
+ if (variant === "xxx") {
344
+ return offset;
345
+ }
346
+ throw new Error(
347
+ `temporal-fmt: token "${variant}" cannot represent the offset "${offset}", which has a seconds component. None of the X/XX/XXX/x/xx tokens support offset seconds; use "xxx" instead, which formats the full offset unchanged.`
348
+ );
349
+ }
350
+ const sign = offset[0];
351
+ const hours = offset.slice(1, 3);
352
+ const minutes = offset.slice(4, 6);
353
+ switch (variant) {
354
+ case "X":
355
+ case "x":
356
+ return minutes === "00" ? `${sign}${hours}` : `${sign}${hours}${minutes}`;
357
+ case "XX":
358
+ case "xx":
359
+ return `${sign}${hours}${minutes}`;
360
+ case "XXX":
361
+ case "xxx":
362
+ return `${sign}${hours}:${minutes}`;
363
+ }
364
+ }
365
+ var TOKENS = [
366
+ ["yyyy", (t) => pad(t.year, 4), "year"],
367
+ ["yy", (t) => {
368
+ if (t.year < 0) {
369
+ throw new Error(
370
+ `temporal-fmt: token "yy" doesn't support negative years (got ${t.year}), since truncating to 2 digits would make it indistinguishable from a positive year. Use "yyyy" instead.`
371
+ );
372
+ }
373
+ return pad(t.year % 100, 2);
374
+ }, "year"],
375
+ // Unpadded year — no minimum width, unlike yyyy's fixed 4 digits.
376
+ // pad(n, 0) still does the right thing here: Math.abs(n) with no
377
+ // padStart floor just yields the plain digit string, and the sign
378
+ // handling (split off before padding) already covers negative years,
379
+ // so this doesn't need its own sign branch the way "yy" does.
380
+ ["y", (t) => pad(t.year, 0), "year"],
381
+ ["MMMM", (t, locale) => {
382
+ const custom = getCustomVocab(locale);
383
+ return localeAwareName(t, locale, { month: "long" }, "month", custom?.monthLong, t.month - 1);
384
+ }, "month"],
385
+ ["MMM", (t, locale) => {
386
+ const custom = getCustomVocab(locale);
387
+ return localeAwareName(t, locale, { month: "short" }, "month", custom?.monthShort, t.month - 1);
388
+ }, "month"],
389
+ ["MM", (t) => pad(t.month, 2), "month"],
390
+ ["M", (t) => String(t.month), "month"],
391
+ ["dd", (t) => pad(t.day, 2), "day"],
392
+ ["d", (t) => String(t.day), "day"],
393
+ ["EEEE", (t, locale) => {
394
+ const custom = getCustomVocab(locale);
395
+ return localeAwareName(t, locale, { weekday: "long" }, "weekday", custom?.weekdayLong, t.dayOfWeek - 1);
396
+ }, "dayOfWeek"],
397
+ ["EEE", (t, locale) => {
398
+ const custom = getCustomVocab(locale);
399
+ return localeAwareName(t, locale, { weekday: "short" }, "weekday", custom?.weekdayShort, t.dayOfWeek - 1);
400
+ }, "dayOfWeek"],
401
+ ["HH", (t) => pad(t.hour, 2), "hour"],
402
+ ["H", (t) => String(t.hour), "hour"],
403
+ ["hh", (t) => pad(t.hour % 12 || 12, 2), "hour"],
404
+ ["h", (t) => String(t.hour % 12 || 12), "hour"],
405
+ ["mm", (t) => pad(t.minute, 2), "minute"],
406
+ ["m", (t) => String(t.minute), "minute"],
407
+ ["ss", (t) => pad(t.second, 2), "second"],
408
+ ["s", (t) => String(t.second), "second"],
409
+ // Fractional-second tokens, S through SSSSSSSSS (1-9 digits). Each token
410
+ // formats a slice of the same underlying nanosecond-of-second value —
411
+ // combining millisecond/microsecond/nanosecond into one 9-digit number
412
+ // and truncating to the token's width — so "SSS" keeps meaning exactly
413
+ // what it always meant (3-digit milliseconds) while wider tokens expose
414
+ // the precision Temporal actually carries. formatFraction below is the
415
+ // shared implementation; see its comment for the truncate-not-round
416
+ // rule and why.
417
+ ["SSSSSSSSS", (t) => pad.fraction(t, 9), "millisecond"],
418
+ ["SSSSSSSS", (t) => pad.fraction(t, 8), "millisecond"],
419
+ ["SSSSSSS", (t) => pad.fraction(t, 7), "millisecond"],
420
+ ["SSSSSS", (t) => pad.fraction(t, 6), "millisecond"],
421
+ ["SSSSS", (t) => pad.fraction(t, 5), "millisecond"],
422
+ ["SSSS", (t) => pad.fraction(t, 4), "millisecond"],
423
+ ["SSS", (t) => pad.fraction(t, 3), "millisecond"],
424
+ ["SS", (t) => pad.fraction(t, 2), "millisecond"],
425
+ ["S", (t) => pad.fraction(t, 1), "millisecond"],
426
+ // dayPeriod text is locale-specific (AM/PM in en-US, م/ص in ar-EG) but
427
+ // still needs .hour on the input to compute which period it is
428
+ ["a", (t, locale) => dayPeriodPart(t.hour, locale), "hour"],
429
+ ["zzz", (t) => t.timeZoneId, "timeZoneId"],
430
+ // Numeric UTC offset tokens (date-fns/Unicode-LDML family). Only
431
+ // ZonedDateTime carries an offset, so the field check in format() throws
432
+ // the same "requires offset, which this Temporal object doesn't have"
433
+ // error zzz throws on PlainDate/PlainTime/PlainDateTime — same
434
+ // validation path, just a different field name. See formatOffset above
435
+ // for the per-variant width and Z/numeric distinction.
436
+ ["xxx", (t) => formatOffset(t.offset, "xxx"), "offset"],
437
+ ["xx", (t) => formatOffset(t.offset, "xx"), "offset"],
438
+ ["X", (t) => formatOffset(t.offset, "X"), "offset"],
439
+ ["XX", (t) => formatOffset(t.offset, "XX"), "offset"],
440
+ ["XXX", (t) => formatOffset(t.offset, "XXX"), "offset"],
441
+ ["x", (t) => formatOffset(t.offset, "x"), "offset"],
442
+ // Ordinal day (1st, 2nd, 3rd, ... 21st). English suffix rules only —
443
+ // locale-aware ordinals ("2." in de-DE, "2日" in ja-JP) are out of scope,
444
+ // since the rest of this library routes locale-specific names through
445
+ // Intl.DateTimeFormat, and Intl has no part type for ordinals. Format-only:
446
+ // the suffix isn't structurally distinguishable from a literal in a parse
447
+ // context (a "st"/"nd"/"rd"/"th" suffix isn't a digit and would collide
448
+ // with any adjacent literal text), so there's no good way to read it back.
449
+ ["do", (t) => {
450
+ const day = t.day;
451
+ const lastDigit = day % 10;
452
+ const lastTwoDigits = day % 100;
453
+ if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
454
+ return day + "th";
455
+ }
456
+ if (lastDigit === 1) return day + "st";
457
+ if (lastDigit === 2) return day + "nd";
458
+ if (lastDigit === 3) return day + "rd";
459
+ return day + "th";
460
+ }, "day"],
461
+ // Quarter computed from month: 1-3=Q1, 4-6=Q2, 7-9=Q3, 10-12=Q4.
462
+ // `Q` is plain numeric, `QQQ` renders as "Q3" — same convention as
463
+ // date-fns's `Q` and `QQQ` for parity with the most common prior art.
464
+ // Both format and parse; parse() cross-checks Q/QQQ against the parsed
465
+ // month in the same spirit as the EEEE-vs-date cross-check.
466
+ ["Q", (t) => String(Math.ceil(t.month / 3)), "month"],
467
+ ["QQQ", (t) => "Q" + Math.ceil(t.month / 3), "month"],
468
+ // ISO 8601 week and week-numbering year. Both are format-only — parsing
469
+ // "ww"/"RRRR" back into a real date requires resolving an ISO week + a
470
+ // weekday (or some other disambiguator) to a specific date, which is a
471
+ // different parsing surface than the token-based parse() here. The
472
+ // ISO-week year (RRRR) can differ from the calendar year at the boundary:
473
+ // Dec 29-31 often belong to week 1 of the *next* year; Jan 1-3 often
474
+ // belong to week 52/53 of the *previous* year. See isoWeekYearAndWeek().
475
+ ["ww", (t) => {
476
+ const { week } = isoWeekYearAndWeek(t.year, t.month, t.day, t.dayOfWeek);
477
+ return pad(week, 2);
478
+ }, "dayOfWeek"],
479
+ ["RRRR", (t) => {
480
+ const { isoYear } = isoWeekYearAndWeek(t.year, t.month, t.day, t.dayOfWeek);
481
+ return pad(isoYear, 4);
482
+ }, "dayOfWeek"],
483
+ // Day of year — number of days since Jan 1 (1-366). Three widths:
484
+ // D — unpadded (1, 2, 366)
485
+ // DD — 2-digit minimum (zero-padded if <100)
486
+ // DDD — 3-digit zero-padded (001, 002, 366)
487
+ // Format-only — parsing day-of-year requires resolving against a year,
488
+ // which is a different shape from the token-based parse() surface.
489
+ // The dayOfYearHelper() in calendarUtils.ts covers the same field for
490
+ // callers who need the numeric value.
491
+ ["D", (t) => String(dayOfYear(t.year, t.month, t.day)), "day"],
492
+ ["DD", (t) => pad(dayOfYear(t.year, t.month, t.day), 2), "day"],
493
+ ["DDD", (t) => pad(dayOfYear(t.year, t.month, t.day), 3), "day"],
494
+ // Stand-alone month — uses Intl's stand-alone form. In most locales
495
+ // (en, fr, de, es) this is identical to MMMM/MMM. In Slavic/Baltic
496
+ // locales (cs, sk, pl, ru) the stand-alone form differs from the
497
+ // format form (nominative vs genitive case). LLLL = long, LLL = short.
498
+ ["LLLL", (t, locale) => {
499
+ const custom = getCustomVocab(locale);
500
+ return localeAwareName(t, locale, { month: "long" }, "month", custom?.monthLong, t.month - 1);
501
+ }, "month"],
502
+ ["LLL", (t, locale) => {
503
+ const custom = getCustomVocab(locale);
504
+ return localeAwareName(t, locale, { month: "short" }, "month", custom?.monthShort, t.month - 1);
505
+ }, "month"],
506
+ // Stand-alone weekday — same pattern as stand-alone month but for
507
+ // weekday names. cccc = long, ccc = short.
508
+ ["cccc", (t, locale) => {
509
+ const custom = getCustomVocab(locale);
510
+ return localeAwareName(t, locale, { weekday: "long" }, "weekday", custom?.weekdayLong, t.dayOfWeek - 1);
511
+ }, "dayOfWeek"],
512
+ ["ccc", (t, locale) => {
513
+ const custom = getCustomVocab(locale);
514
+ return localeAwareName(t, locale, { weekday: "short" }, "weekday", custom?.weekdayShort, t.dayOfWeek - 1);
515
+ }, "dayOfWeek"],
516
+ // Era — locale-aware ("AD"/"BC" in en, "ap. J.-C."/"av. J.-C." in fr).
517
+ // GGGG = long, G = short. Format-only.
518
+ ["GGGG", (t, locale) => {
519
+ return intlPart(t, locale, { era: "long" }, "era");
520
+ }, "year"],
521
+ ["G", (t, locale) => {
522
+ return intlPart(t, locale, { era: "short" }, "era");
523
+ }, "year"],
524
+ // Localized timezone name — uses Intl's longLocalized/short timezone
525
+ // name option. Format-only — these names are locale-dependent and
526
+ // vary by season (EST vs EDT), so parsing them back requires a
527
+ // lookup table that isn't practical to ship.
528
+ ["zzzz", (t, locale) => {
529
+ return intlPart(t, locale, { timeZoneName: "longGeneric" }, "timeZoneName");
530
+ }, "timeZoneId"],
531
+ ["z", (t, locale) => {
532
+ return intlPart(t, locale, { timeZoneName: "short" }, "timeZoneName");
533
+ }, "timeZoneId"]
534
+ ];
535
+
536
+ // src/tokenize.ts
537
+ var SORTED_TOKEN_STRINGS = TOKENS.map(([tok]) => tok).sort((a, b) => b.length - a.length);
538
+ function tokenize(format2) {
539
+ const pieces = [];
540
+ let i = 0;
541
+ while (i < format2.length) {
542
+ const ch = format2[i];
543
+ if (ch === "'") {
544
+ if (format2[i + 1] === "'") {
545
+ appendLiteral(pieces, "'");
546
+ i += 2;
547
+ continue;
548
+ }
549
+ let j = i + 1;
550
+ let literal = "";
551
+ let closed = false;
552
+ while (j < format2.length) {
553
+ if (format2[j] === "'") {
554
+ if (format2[j + 1] === "'") {
555
+ literal += "'";
556
+ j += 2;
557
+ continue;
558
+ }
559
+ closed = true;
560
+ j += 1;
561
+ break;
562
+ }
563
+ literal += format2[j];
564
+ j += 1;
565
+ }
566
+ if (!closed) {
567
+ throw new FormatSyntaxError({
568
+ format: format2,
569
+ message: `temporal-fmt: unterminated quote in format string "${format2}"`
570
+ });
571
+ }
572
+ appendLiteral(pieces, literal);
573
+ i = j;
574
+ continue;
575
+ }
576
+ const match = SORTED_TOKEN_STRINGS.find((tok) => format2.startsWith(tok, i));
577
+ if (match) {
578
+ const runChar = match[match.length - 1];
579
+ if (format2[i + match.length] === runChar) {
580
+ let end = i + match.length;
581
+ while (format2[end] === runChar) end += 1;
582
+ throw new UnknownTokenError({
583
+ format: format2,
584
+ token: format2.slice(i, end),
585
+ message: `temporal-fmt: "${format2.slice(i, end)}" in format string "${format2}" isn't a recognized token \u2014 did you mean "${match}"?`
586
+ });
587
+ }
588
+ pieces.push({ kind: "token", value: match });
589
+ i += match.length;
590
+ continue;
591
+ }
592
+ appendLiteral(pieces, ch);
593
+ i += 1;
594
+ }
595
+ return pieces;
596
+ }
597
+ function appendLiteral(pieces, value) {
598
+ const last = pieces[pieces.length - 1];
599
+ if (last && last.kind === "literal") {
600
+ last.value += value;
601
+ } else {
602
+ pieces.push({ kind: "literal", value });
603
+ }
604
+ }
605
+
606
+ // src/constants.ts
607
+ var MAX_FORMAT_LENGTH = 1e3;
608
+
609
+ // src/numbering.ts
610
+ var SUPPORTED_NUMBERING_SYSTEMS = /* @__PURE__ */ new Set([
611
+ "latn",
612
+ "arab",
613
+ "deva",
614
+ "beng",
615
+ "guru",
616
+ "gujr",
617
+ "orya",
618
+ "tamldec",
619
+ "telu",
620
+ "knda",
621
+ "mlym",
622
+ "fullwide",
623
+ "hanidec"
624
+ ]);
625
+ var digitMapCache = /* @__PURE__ */ new Map();
626
+ function getDigitMap(system) {
627
+ let map = digitMapCache.get(system);
628
+ if (map) return map;
629
+ /* c8 ignore start @preserve -- this branch is dead by construction, not
630
+ just untested: both callers (convertDigits, convertDigitsToAscii)
631
+ already bail out early on system === 'latn' before ever calling
632
+ getDigitMap, so it never actually gets invoked with 'latn'. keeping
633
+ it anyway as a defensive fallback rather than betting that stays
634
+ true forever */
635
+ if (system === "latn") {
636
+ map = {};
637
+ for (let i = 0; i < 10; i++) map[String(i)] = String(i);
638
+ } else {
639
+ /* c8 ignore stop @preserve */
640
+ const fmt = new Intl.NumberFormat("en-US-u-nu-" + system, { useGrouping: false });
641
+ map = {};
642
+ for (let i = 0; i < 10; i++) {
643
+ map[String(i)] = fmt.format(i);
644
+ }
645
+ }
646
+ digitMapCache.set(system, map);
647
+ return map;
648
+ }
649
+ function convertDigits(s, system) {
650
+ if (system === "latn") return s;
651
+ if (!SUPPORTED_NUMBERING_SYSTEMS.has(system)) {
652
+ throw new InvalidLocaleError({ actual: system, reason: `numbering system "${system}" is not supported. Supported: ${[...SUPPORTED_NUMBERING_SYSTEMS].join(", ")}.` });
653
+ }
654
+ const map = getDigitMap(system);
655
+ let result = "";
656
+ for (const ch of s) {
657
+ if (ch >= "0" && ch <= "9") {
658
+ result += map[ch] ?? ch;
659
+ } else {
660
+ result += ch;
661
+ }
662
+ }
663
+ return result;
664
+ }
665
+ function applyNumbering(s, options) {
666
+ const system = options.numberingSystem ?? "latn";
667
+ if (system === "latn") return s;
668
+ return convertDigits(s, system);
669
+ }
670
+
671
+ // src/format.ts
672
+ var HANDLER_BY_TOKEN = new Map(TOKENS.map(([tok, fn, field]) => [tok, { fn, field }]));
673
+ var tokenizeCache = /* @__PURE__ */ new Map();
674
+ var MAX_TOKENIZE_CACHE_SIZE = 500;
675
+ function getPieces(formatStr) {
676
+ let pieces = tokenizeCache.get(formatStr);
677
+ if (pieces) return pieces;
678
+ if (tokenizeCache.size >= MAX_TOKENIZE_CACHE_SIZE) {
679
+ const oldestKey = tokenizeCache.keys().next().value;
680
+ if (oldestKey !== void 0) tokenizeCache.delete(oldestKey);
681
+ }
682
+ pieces = tokenize(formatStr);
683
+ tokenizeCache.set(formatStr, pieces);
684
+ return pieces;
685
+ }
686
+ function format(temporal, formatStr, options = {}) {
687
+ if (formatStr.length > MAX_FORMAT_LENGTH) {
688
+ throw new FormatSyntaxError({
689
+ format: formatStr,
690
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
691
+ });
692
+ }
693
+ const locale = options.locale ?? DEFAULT_LOCALE;
694
+ const pieces = getPieces(formatStr);
695
+ let result = "";
696
+ for (const piece of pieces) {
697
+ if (piece.kind === "literal") {
698
+ result += piece.value;
699
+ continue;
700
+ }
701
+ const handler = HANDLER_BY_TOKEN.get(piece.value);
702
+ /* c8 ignore start @preserve -- unreachable: tokenize() only ever emits
703
+ tokens present in TOKENS, and HANDLER_BY_TOKEN is built from that
704
+ same list, so every token piece.value can hold already has an entry
705
+ here. Same defensive-guard category as the twin checks in
706
+ analyze.ts/formatDuration.ts. */
707
+ if (!handler) {
708
+ throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
709
+ }
710
+ /* c8 ignore stop @preserve */
711
+ if (temporal[handler.field] === void 0) {
712
+ throw new FormatSyntaxError({
713
+ format: formatStr,
714
+ token: piece.value,
715
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
716
+ });
717
+ }
718
+ result += handler.fn(temporal, locale);
719
+ }
720
+ return applyNumbering(result, options);
721
+ }
722
+ function formatToParts(temporal, formatStr, options = {}) {
723
+ if (formatStr.length > MAX_FORMAT_LENGTH) {
724
+ throw new FormatSyntaxError({
725
+ format: formatStr,
726
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
727
+ });
728
+ }
729
+ const locale = options.locale ?? DEFAULT_LOCALE;
730
+ const pieces = getPieces(formatStr);
731
+ const result = [];
732
+ for (const piece of pieces) {
733
+ if (piece.kind === "literal") {
734
+ const last = result[result.length - 1];
735
+ /* c8 ignore start @preserve -- unreachable through tokenize()'s own
736
+ output: appendLiteral() in tokenize.ts already merges every
737
+ adjacent literal character before pieces ever reaches here, so
738
+ two 'literal' pieces never sit back-to-back in the array this
739
+ loop walks. Kept as defense-in-depth in case that invariant ever
740
+ changes upstream. */
741
+ if (last && last.type === "literal") {
742
+ last.value += piece.value;
743
+ } else {
744
+ /* c8 ignore stop @preserve */
745
+ result.push({ type: "literal", value: piece.value });
746
+ }
747
+ continue;
748
+ }
749
+ const handler = HANDLER_BY_TOKEN.get(piece.value);
750
+ /* c8 ignore start @preserve -- unreachable, see the note in format() above */
751
+ if (!handler) {
752
+ throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
753
+ }
754
+ /* c8 ignore stop @preserve */
755
+ if (temporal[handler.field] === void 0) {
756
+ throw new FormatSyntaxError({
757
+ format: formatStr,
758
+ token: piece.value,
759
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
760
+ });
761
+ }
762
+ result.push({ type: "token", value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });
763
+ }
764
+ return result;
765
+ }
766
+ function compileFormat(formatStr) {
767
+ if (formatStr.length > MAX_FORMAT_LENGTH) {
768
+ throw new FormatSyntaxError({
769
+ format: formatStr,
770
+ message: `temporal-fmt: format string exceeds maximum length of ${MAX_FORMAT_LENGTH} characters (got ${formatStr.length}).`
771
+ });
772
+ }
773
+ const pieces = getPieces(formatStr);
774
+ return {
775
+ formatStr,
776
+ pieces,
777
+ format(temporal, options = {}) {
778
+ const locale = options.locale ?? DEFAULT_LOCALE;
779
+ let result = "";
780
+ for (const piece of pieces) {
781
+ if (piece.kind === "literal") {
782
+ result += piece.value;
783
+ continue;
784
+ }
785
+ const handler = HANDLER_BY_TOKEN.get(piece.value);
786
+ if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
787
+ if (temporal[handler.field] === void 0) {
788
+ throw new FormatSyntaxError({
789
+ format: formatStr,
790
+ token: piece.value,
791
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
792
+ });
793
+ }
794
+ result += handler.fn(temporal, locale);
795
+ }
796
+ return applyNumbering(result, options);
797
+ },
798
+ formatToParts(temporal, options = {}) {
799
+ const locale = options.locale ?? DEFAULT_LOCALE;
800
+ const out = [];
801
+ for (const piece of pieces) {
802
+ if (piece.kind === "literal") {
803
+ const last = out[out.length - 1];
804
+ if (last && last.type === "literal") last.value += piece.value;
805
+ else out.push({ type: "literal", value: piece.value });
806
+ continue;
807
+ }
808
+ const handler = HANDLER_BY_TOKEN.get(piece.value);
809
+ if (!handler) throw new UnknownTokenError({ token: piece.value, format: formatStr, message: `temporal-fmt: unknown token "${piece.value}"` });
810
+ if (temporal[handler.field] === void 0) {
811
+ throw new FormatSyntaxError({
812
+ format: formatStr,
813
+ token: piece.value,
814
+ message: `temporal-fmt: token "${piece.value}" requires "${handler.field}", which this Temporal object doesn't have. (e.g. PlainDate has no time fields, PlainTime has no date fields)`
815
+ });
816
+ }
817
+ out.push({ type: "token", value: applyNumbering(handler.fn(temporal, locale), options), token: piece.value });
818
+ }
819
+ return out;
820
+ }
821
+ };
822
+ }
823
+ function _getPieces(formatStr) {
824
+ return getPieces(formatStr);
825
+ }
826
+ function _handlerFor(token) {
827
+ return HANDLER_BY_TOKEN.get(token);
828
+ }
829
+ // Annotate the CommonJS export names for ESM import in node:
830
+ 0 && (module.exports = {
831
+ _getPieces,
832
+ _handlerFor,
833
+ compileFormat,
834
+ format,
835
+ formatToParts
836
+ });
39
837
  //# sourceMappingURL=format.cjs.map