@northguild/gmt 1.14.2 → 1.15.0

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 (137) hide show
  1. package/README.md +43 -13
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1 -0
  4. package/dist/plain/calculate/diffDateAsDuration.js +1 -1
  5. package/dist/plain/calculate/index.d.ts +2 -1
  6. package/dist/plain/calculate/index.js +2 -1
  7. package/dist/plain/calculate/{weekOfYear.d.ts → weekOfYearForDate.d.ts} +0 -14
  8. package/dist/plain/calculate/weekOfYearForDate.js +28 -0
  9. package/dist/plain/calculate/weekOfYearForDateTime.d.ts +14 -0
  10. package/dist/plain/calculate/{weekOfYear.js → weekOfYearForDateTime.js} +1 -27
  11. package/dist/plain/format/formatCalendar.d.ts +2 -2
  12. package/dist/plain/format/formatDate.d.ts +3 -2
  13. package/dist/plain/format/formatDate.js +1 -1
  14. package/dist/plain/format/formatDateRange.d.ts +3 -2
  15. package/dist/plain/format/formatDateRange.js +1 -1
  16. package/dist/plain/format/formatDateTime.d.ts +3 -2
  17. package/dist/plain/format/formatDateTime.js +1 -1
  18. package/dist/plain/format/formatDateTimeRange.d.ts +3 -2
  19. package/dist/plain/format/formatDateTimeRange.js +1 -1
  20. package/dist/plain/format/formatDateTimeToParts.d.ts +3 -2
  21. package/dist/plain/format/formatDateTimeToParts.js +1 -1
  22. package/dist/plain/format/formatDateToParts.d.ts +3 -2
  23. package/dist/plain/format/formatDateToParts.js +1 -1
  24. package/dist/plain/format/formatRelativeDate.d.ts +3 -14
  25. package/dist/plain/format/formatRelativeDateTime.d.ts +3 -14
  26. package/dist/plain/format/formatRelativeTime.d.ts +3 -14
  27. package/dist/plain/format/formatTime.d.ts +2 -1
  28. package/dist/plain/get/getNowUnit.d.ts +2 -3
  29. package/dist/plain/interval/splitIntervalByUnitDate.js +1 -1
  30. package/dist/regex/calendar-date.d.ts +19 -0
  31. package/dist/regex/calendar-date.js +19 -10
  32. package/dist/regex/calendar-zoned-date-time.d.ts +45 -0
  33. package/dist/regex/calendar-zoned-date-time.js +45 -40
  34. package/dist/regex/date-time.d.ts +18 -0
  35. package/dist/regex/date-time.js +18 -1
  36. package/dist/regex/date.d.ts +16 -0
  37. package/dist/regex/date.js +16 -1
  38. package/dist/regex/http-date.d.ts +15 -0
  39. package/dist/regex/http-date.js +15 -6
  40. package/dist/regex/leap-second.d.ts +12 -0
  41. package/dist/regex/leap-second.js +12 -1
  42. package/dist/regex/rfc-2822.d.ts +18 -0
  43. package/dist/regex/rfc-2822.js +18 -8
  44. package/dist/regex/rfc-3339.d.ts +19 -0
  45. package/dist/regex/rfc-3339.js +19 -6
  46. package/dist/regex/sql-date-time.d.ts +14 -0
  47. package/dist/regex/sql-date-time.js +14 -6
  48. package/dist/regex/time-zone-like.d.ts +21 -0
  49. package/dist/regex/time-zone-like.js +21 -0
  50. package/dist/regex/time.d.ts +59 -0
  51. package/dist/regex/time.js +59 -3
  52. package/dist/regex/unix.d.ts +21 -0
  53. package/dist/regex/unix.js +21 -0
  54. package/dist/regex/utc-date-time.d.ts +20 -0
  55. package/dist/regex/utc-date-time.js +20 -1
  56. package/dist/types/calendar-options.d.ts +11 -0
  57. package/dist/types/calendar-options.js +0 -0
  58. package/dist/types/datetime-format-options.d.ts +6 -0
  59. package/dist/types/datetime-format-options.js +0 -0
  60. package/dist/types/duration-unit.d.ts +5 -0
  61. package/dist/types/duration-unit.js +0 -0
  62. package/dist/types/index.d.ts +11 -0
  63. package/dist/types/index.js +11 -0
  64. package/dist/types/now-unit.d.ts +5 -0
  65. package/dist/types/now-unit.js +0 -0
  66. package/dist/types/relative-date-time-unit.d.ts +6 -0
  67. package/dist/types/relative-date-time-unit.js +0 -0
  68. package/dist/types/relative-date-unit.d.ts +5 -0
  69. package/dist/types/relative-date-unit.js +0 -0
  70. package/dist/types/relative-time-options.d.ts +18 -0
  71. package/dist/types/relative-time-options.js +0 -0
  72. package/dist/types/relative-time-unit.d.ts +5 -0
  73. package/dist/types/relative-time-unit.js +0 -0
  74. package/dist/types/relative-unit.d.ts +6 -0
  75. package/dist/types/relative-unit.js +0 -0
  76. package/dist/types/unix-now-unit.d.ts +5 -0
  77. package/dist/types/unix-now-unit.js +0 -0
  78. package/dist/types/utc-now-unit.d.ts +5 -0
  79. package/dist/types/utc-now-unit.js +0 -0
  80. package/dist/unix/calculate/startOrEndOfUnix.d.ts +18 -0
  81. package/dist/unix/calculate/startOrEndOfUnix.js +18 -0
  82. package/dist/unix/format/formatCalendarUnix.d.ts +27 -1
  83. package/dist/unix/format/formatRelativeUnix.d.ts +1 -8
  84. package/dist/unix/format/formatUnix.d.ts +19 -0
  85. package/dist/unix/format/formatUnix.js +19 -0
  86. package/dist/unix/get/getUnixNowUnit.d.ts +2 -3
  87. package/dist/unix/parse/parseUnitFromUnix.d.ts +24 -0
  88. package/dist/unix/validate/isValidUnixUnit.d.ts +15 -0
  89. package/dist/utc/calculate/startOrEndOfUtc.d.ts +17 -0
  90. package/dist/utc/calculate/startOrEndOfUtc.js +17 -0
  91. package/dist/utc/format/formatCalendarUtc.d.ts +2 -2
  92. package/dist/utc/format/formatRelativeUtc.d.ts +2 -12
  93. package/dist/utc/format/formatUtc.d.ts +33 -0
  94. package/dist/utc/format/formatUtc.js +17 -0
  95. package/dist/utc/get/getUtcNowUnit.d.ts +2 -3
  96. package/dist/utc/parse/parseUnitFromUtc.d.ts +23 -0
  97. package/dist/zoned/format/formatRelativeZoned.d.ts +1 -8
  98. package/dist/zoned/format/formatTimeZoneName.d.ts +15 -0
  99. package/dist/zoned/format/formatZonedDateTime.d.ts +2 -1
  100. package/dist/zoned/format/formatZonedRange.d.ts +2 -1
  101. package/dist/zoned/format/formatZonedToParts.d.ts +2 -1
  102. package/dist/zoned/get/getDstTransitions.d.ts +18 -2
  103. package/dist/zoned/get/getZonedNowUnit.d.ts +24 -0
  104. package/dist/zoned/interval/intervalDifferenceZoned.d.ts +3 -3
  105. package/dist/zoned/interval/intervalDifferenceZoned.js +3 -3
  106. package/dist/zoned/interval/intervalXorZoned.d.ts +6 -4
  107. package/dist/zoned/interval/intervalXorZoned.js +6 -4
  108. package/dist/zoned/parse/getZonedOffsetAs.d.ts +11 -0
  109. package/dist/zoned/parse/parseUnitFromZoned.d.ts +24 -0
  110. package/package.json +25 -7
  111. package/skills/gmt-arithmetic/SKILL.md +83 -0
  112. package/skills/gmt-basics/SKILL.md +80 -0
  113. package/skills/gmt-integration/SKILL.md +73 -0
  114. package/skills/gmt-timezone/SKILL.md +80 -0
  115. package/skills/_artifacts/domain_map.yaml +0 -547
  116. package/skills/_artifacts/skill_spec.md +0 -67
  117. package/skills/_artifacts/skill_tree.yaml +0 -278
  118. package/skills/api-expansion-workflow/SKILL.md +0 -52
  119. package/skills/app-integration/SKILL.md +0 -38
  120. package/skills/calculate-dates/SKILL.md +0 -432
  121. package/skills/compare-dates/SKILL.md +0 -412
  122. package/skills/convert-types/SKILL.md +0 -305
  123. package/skills/durations/SKILL.md +0 -418
  124. package/skills/format-date-time/SKILL.md +0 -486
  125. package/skills/format-date-time/references/format-api.md +0 -34
  126. package/skills/format-relative-time/SKILL.md +0 -223
  127. package/skills/get-current/SKILL.md +0 -229
  128. package/skills/interval-ops/SKILL.md +0 -432
  129. package/skills/issue-creation/SKILL.md +0 -118
  130. package/skills/lint-package-suggestion/SKILL.md +0 -37
  131. package/skills/new-method-implementation/SKILL.md +0 -71
  132. package/skills/parse-date-time/SKILL.md +0 -349
  133. package/skills/pr-contribution/SKILL.md +0 -180
  134. package/skills/unit-test-generation/SKILL.md +0 -56
  135. package/skills/validate-dates/SKILL.md +0 -438
  136. package/skills/zoned-date-ops/SKILL.md +0 -422
  137. package/skills/zoned-date-ops/references/timezones.md +0 -13
@@ -1,486 +0,0 @@
1
- ---
2
- name: format-date-time
3
- description: >
4
- Format plain date/time values for display with locale and Intl.DateTimeFormat
5
- options. Use formatDate, formatTime, formatDateTime for absolute formatting;
6
- formatDateRange, formatDateTimeRange for a locale-elided range between two
7
- plain values; formatDateToParts, formatDateTimeToParts, formatZonedToParts for
8
- locale-ordered { type, value } parts; formatRelativeDate, formatRelativeTime,
9
- formatRelativeDateTime for human-friendly relative output ("yesterday", "in 2
10
- hours"); formatCalendar, formatCalendarZoned, formatCalendarUnix,
11
- formatCalendarUtc for a relative day label + time-of-day ("tomorrow at 2:30
12
- PM") — distinct from the elapsed-time formatRelative* phrasing. Also
13
- getLocaleEraNames, getLocaleMonthNames, getLocaleWeekdayNames,
14
- getLocaleMeridiems for standalone locale calendar-name lookups (Luxon Info
15
- equivalent). Also formatRfc2822, formatHttp, formatSql, formatRfc3339 for
16
- fixed, non-locale interchange grammars (email/HTTP headers, SQL, RFC 3339) —
17
- no locale argument.
18
- sources:
19
- - 'northguild/gmt:packages/gmt/src/plain/format/index.ts'
20
- - 'northguild/gmt:packages/gmt/src/plain/format/formatDateRange.ts'
21
- - 'northguild/gmt:packages/gmt/src/plain/format/formatDateTimeRange.ts'
22
- - 'northguild/gmt:packages/gmt/src/zoned/format/formatZonedRange.ts'
23
- - 'northguild/gmt:packages/gmt/src/plain/format/formatDateToParts.ts'
24
- - 'northguild/gmt:packages/gmt/src/plain/format/formatDateTimeToParts.ts'
25
- - 'northguild/gmt:packages/gmt/src/zoned/format/formatZonedToParts.ts'
26
- - 'northguild/gmt:packages/gmt/src/plain/locale/index.ts'
27
- - 'northguild/gmt:packages/gmt/src/plain/locale/getLocaleEraNames.ts'
28
- - 'northguild/gmt:packages/gmt/src/plain/locale/getLocaleMonthNames.ts'
29
- - 'northguild/gmt:packages/gmt/src/plain/locale/getLocaleWeekdayNames.ts'
30
- - 'northguild/gmt:packages/gmt/src/plain/locale/getLocaleMeridiems.ts'
31
- - 'northguild/gmt:packages/gmt/src/zoned/format/formatRfc2822.ts'
32
- - 'northguild/gmt:packages/gmt/src/utc/format/formatHttp.ts'
33
- - 'northguild/gmt:packages/gmt/src/plain/format/formatSql.ts'
34
- - 'northguild/gmt:packages/gmt/src/zoned/format/formatRfc3339.ts'
35
- - 'northguild/gmt:packages/gmt/src/plain/format/formatCalendar.ts'
36
- - 'northguild/gmt:packages/gmt/src/zoned/format/formatCalendarZoned.ts'
37
- - 'northguild/gmt:packages/gmt/src/unix/format/formatCalendarUnix.ts'
38
- - 'northguild/gmt:packages/gmt/src/utc/format/formatCalendarUtc.ts'
39
- - 'northguild/gmt:packages/gmt/src/internal/joinDateTimeConnector.ts'
40
- metadata:
41
- type: core
42
- library: '@northguild/gmt'
43
- library_version: '1.14.2'
44
- ---
45
-
46
- # Format Date/Time
47
-
48
- Use this skill when you need to format date or time values for display to users.
49
-
50
- ## Setup
51
-
52
- ```ts
53
- import {
54
- formatDate,
55
- formatTime,
56
- formatDateTime,
57
- formatRelativeDate,
58
- formatRelativeTime,
59
- formatRelativeDateTime,
60
- } from "@northguild/gmt";
61
- ```
62
-
63
- ## Core Patterns
64
-
65
- ### Format date with default locale
66
-
67
- ```ts
68
- const formatted = formatDate("2024-03-15"); // "3/15/2024" (US format)
69
- ```
70
-
71
- ### Format date with specific locale
72
-
73
- ```ts
74
- const usDate = formatDate("2024-03-15", "en-US"); // "3/15/2024"
75
- const ukDate = formatDate("2024-03-15", "en-GB"); // "15/03/2024"
76
- const deDate = formatDate("2024-03-15", "de-DE"); // "15.3.2024"
77
- ```
78
-
79
- ### Format time with locale
80
-
81
- ```ts
82
- const time = formatTime("14:30:45"); // "2:30:45 PM"
83
- const timeUk = formatTime("14:30:45", "en-GB"); // "14:30:45"
84
- ```
85
-
86
- ### Format datetime with locale
87
-
88
- ```ts
89
- const formatted = formatDateTime("2024-03-15T14:30:45"); // "3/15/2024, 2:30:45 PM"
90
- const formattedUk = formatDateTime("2024-03-15T14:30:45", "en-GB"); // "15/03/2024, 14:30:45"
91
- ```
92
-
93
- ### Format with options
94
-
95
- ```ts
96
- import { formatDate } from "@northguild/gmt";
97
-
98
- const formatted = formatDate("2024-03-15", "en-US", {
99
- year: "numeric",
100
- month: "long",
101
- day: "numeric"
102
- }); // "March 15, 2024"
103
- ```
104
-
105
- ### Format a plain date/datetime range
106
-
107
- ```ts
108
- import { formatDateRange, formatDateTimeRange } from "@northguild/gmt";
109
-
110
- formatDateRange("2024-02-03", "2024-02-05", "en-US", { dateStyle: "long" });
111
- // "February 3 – 5, 2024" — Intl elides the shared month/year automatically
112
-
113
- formatDateRange("2024-11-03", "2025-02-10", "en-US", { dateStyle: "long" });
114
- // "November 3, 2024 – February 10, 2025" — no elision once the year differs
115
-
116
- formatDateTimeRange(
117
- "2024-02-03T09:00:00",
118
- "2024-02-03T17:00:00",
119
- "en-US",
120
- { dateStyle: "long", timeStyle: "short" },
121
- );
122
- // "February 3, 2024, 9:00 AM – 5:00 PM" — same day collapses to one date
123
- ```
124
-
125
- These are the plain (timezone-free) counterparts of `formatZonedRange` (zoned
126
- namespace) — same parameter order (`start, end, locale?, options?`) and
127
- option shape, wrapping `Intl.DateTimeFormat.prototype.formatRange` directly
128
- instead of joining two separately-formatted strings by hand. Do not swap
129
- `start`/`end` before calling — a reversed range still formats (it does not
130
- throw or auto-correct), so validate ordering yourself if that matters to the
131
- caller.
132
-
133
- ### Get locale-ordered parts instead of a finished string
134
-
135
- ```ts
136
- import { formatDateToParts, formatDateTimeToParts } from "@northguild/gmt";
137
- import { formatZonedToParts } from "@northguild/gmt";
138
-
139
- formatDateToParts("2024-03-15", "en-US");
140
- // [{ type: "month", value: "3" }, { type: "literal", value: "/" },
141
- // { type: "day", value: "15" }, { type: "literal", value: "/" },
142
- // { type: "year", value: "2024" }]
143
-
144
- formatDateToParts("2024-03-15", "fr-FR");
145
- // day comes before month — same locale-order guarantee formatDate gives you,
146
- // but as parts you can restyle instead of a finished string.
147
- // [{ type: "day", value: "15" }, { type: "literal", value: "/" },
148
- // { type: "month", value: "3" }, { type: "literal", value: "/" },
149
- // { type: "year", value: "2024" }]
150
-
151
- formatZonedToParts(
152
- "2024-03-15T14:30:00-04:00[America/New_York]",
153
- "en-US",
154
- { timeZoneName: "longOffset" },
155
- );
156
- // includes { type: "timeZoneName", value: "GMT-4" }
157
- ```
158
-
159
- Iterate the returned array in order — do not pick out `.find(p => p.type === "month")` and reassemble parts into a fixed order, since that reintroduces the exact locale-ordering bug `formatToParts` exists to avoid (see Common Mistakes below).
160
-
161
- ### Format relative date
162
-
163
- ```ts
164
- import { formatRelativeDate } from "@northguild/gmt";
165
-
166
- const ref = "2024-03-15";
167
- formatRelativeDate("2024-03-12", "en-US", { reference: ref }); // "3 days ago"
168
- formatRelativeDate("2024-03-18", "en-US", { reference: ref }); // "in 3 days"
169
- formatRelativeDate("2024-03-14", "en-US", { reference: ref }); // "yesterday"
170
- formatRelativeDate("2023-03-15", "en-US", { reference: ref }); // "last year"
171
- ```
172
-
173
- ### Format relative time / datetime
174
-
175
- ```ts
176
- import { formatRelativeTime, formatRelativeDateTime } from "@northguild/gmt";
177
-
178
- formatRelativeTime("11:30:00", "en-US", { reference: "12:00:00" });
179
- // "30 minutes ago"
180
-
181
- formatRelativeDateTime("2024-03-15T10:00:00", "en-US", {
182
- reference: "2024-03-15T12:00:00",
183
- }); // "2 hours ago"
184
- ```
185
-
186
- ### Relative format options
187
-
188
- - `reference: string` — required anchor (the same shape as `value`)
189
- - `numeric: "auto" | "always"` — default `"auto"`. `"auto"` produces "yesterday"/"tomorrow"; `"always"` forces "1 day ago"/"in 1 day"
190
- - `style: "long" | "short" | "narrow"` — default `"long"`
191
- - `largestUnit` — override the auto-picked unit (e.g. `"week"` to force "3 weeks ago" instead of "last month")
192
-
193
- ### Format a relative day label + time-of-day (Moment's `.calendar()`)
194
-
195
- ```ts
196
- import { formatCalendar, formatCalendarZoned } from "@northguild/gmt";
197
-
198
- const ref = "2026-03-15T09:00:00";
199
- formatCalendar("2026-03-16T14:30:00", "en-US", { reference: ref });
200
- // "tomorrow at 2:30 PM"
201
-
202
- formatCalendar("2026-03-15T14:30:00", "en-US", { reference: ref });
203
- // "today at 2:30 PM"
204
-
205
- formatCalendar("2026-03-08T14:30:00", "en-US", { reference: ref });
206
- // "March 8, 2026 at 2:30 PM" — 7 days out, beyond the ±6-day threshold,
207
- // falls back to an absolute date + time with no relative wording
208
-
209
- formatCalendarZoned(
210
- "2026-03-16T14:30:00-04:00[America/New_York]",
211
- "de-DE",
212
- { reference: "2026-03-15T09:00:00-04:00[America/New_York]" },
213
- );
214
- // "morgen um 14:30" — the connector ("um") is the locale's own, read from
215
- // Intl's combined date+time pattern, never a hardcoded "at"
216
- ```
217
-
218
- Unlike `formatRelativeDate`/`formatRelativeDateTime` (which auto-pick a unit
219
- and always render an elapsed-time phrase — "in 1 day"), `formatCalendar*`
220
- always renders a day-granularity label plus the time-of-day, and switches to
221
- an absolute date beyond a fixed ±6-day threshold instead of degrading to
222
- "in 9 days". Options: `reference` (default "now"), `timeStyle` ("short"
223
- default, "medium", plus "full" on the zoned/unix/utc variants — not on plain
224
- `formatCalendar`, since a plain value has no real timezone for "full"'s
225
- `timeZoneName` to name; see its JSDoc).
226
-
227
- ### Standalone locale calendar names (no date value needed)
228
-
229
- These return locale-formatted calendar names directly — the GMT equivalent of Luxon's `Info` class — without requiring a date value:
230
-
231
- ```ts
232
- import {
233
- getLocaleEraNames,
234
- getLocaleMonthNames,
235
- getLocaleWeekdayNames,
236
- getLocaleMeridiems,
237
- } from "@northguild/gmt";
238
-
239
- getLocaleEraNames("en-US");
240
- // ["Before Christ", "Anno Domini"]
241
-
242
- getLocaleEraNames("ja-JP", "short");
243
- // ["紀元前", "西暦"]
244
-
245
- getLocaleMonthNames("en-US");
246
- // ["January", "February", ... "December"]
247
-
248
- getLocaleMonthNames("de-DE", "short");
249
- // ["Jan", "Feb", "Mär", ... "Dez"]
250
-
251
- getLocaleWeekdayNames("en-US");
252
- // ["Sunday", "Monday", ... "Saturday"] (locale-first-day order)
253
-
254
- getLocaleWeekdayNames("fr-FR");
255
- // ["lundi", "mardi", ... "dimanche"]
256
-
257
- getLocaleMeridiems("en-US");
258
- // ["AM", "PM"]
259
-
260
- getLocaleMeridiems("zh-CN");
261
- // ["上午", "下午"]
262
- ```
263
-
264
- Options:
265
-
266
- - `getLocaleEraNames(locale, style?)` — 2-element `[BCE-label, CE-label]` Gregorian era names. `style: "long" | "short" | "narrow"` (default `"long"`).
267
- - `getLocaleMonthNames(locale, style?)` — 12 Gregorian month names in calendar order. `style: "long" | "short" | "narrow"` (default `"long"`).
268
- - `getLocaleWeekdayNames(locale, style?)` — 7 weekday names in the locale's **first-day order** (Sunday-first for en-US, Monday-first for fr-FR). This matches `getLocaleDayOfWeek`, where index 0 is the locale's first day of the week.
269
- - `getLocaleMeridiems(locale)` — `[AM-label, PM-label]`; labels are locale-varying (e.g. `en-GB` → `["am","pm"]`, `sv-SE` → `["fm","em"]`, `zh-CN` → `["上午","下午"]`).
270
-
271
- All four return `[]` for an invalid BCP 47 locale tag. If a locale has no distinct BCE/CE era names, `getLocaleEraNames` returns both elements as the same string — the sentinel is reserved for invalid input only.
272
-
273
- ### Named machine formats (email, HTTP, SQL, RFC 3339)
274
-
275
- These are **fixed, non-locale-adaptive grammars** — RFC 5322/RFC 7231 mandate
276
- English weekday/month abbreviations regardless of caller locale, by
277
- specification. This does not contradict the token-formatter exclusion above
278
- (Decision 1): there is no locale-appropriate alternative field order to lose,
279
- because the grammar is a constant, not a display choice. None of these four
280
- take a `locale` argument.
281
-
282
- ```ts
283
- import { formatRfc2822, formatHttp, formatSql, formatRfc3339 } from "@northguild/gmt";
284
-
285
- // Email `Date:` headers (RFC 5322 / RFC 2822) — zoned namespace.
286
- formatRfc2822("2024-03-15T14:30:00-04:00[America/New_York]");
287
- // "Fri, 15 Mar 2024 14:30:00 -0400"
288
-
289
- // HTTP headers (RFC 7231 IMF-fixdate) — utc namespace, always GMT.
290
- formatHttp("2024-03-15T14:30:00Z");
291
- // "Fri, 15 Mar 2024 14:30:00 GMT"
292
-
293
- // ANSI SQL / ODBC datetime literal — plain namespace, no time zone.
294
- formatSql("2024-03-15T14:30:00");
295
- // "2024-03-15 14:30:00"
296
-
297
- // Strict RFC 3339 — zoned namespace; strips the bracketed IANA zone
298
- // GMT's own zoned strings carry, which RFC 3339 does not permit.
299
- formatRfc3339("2024-03-15T14:30:00-04:00[America/New_York]");
300
- // "2024-03-15T14:30:00-04:00"
301
- ```
302
-
303
- Each has a `parse*` counterpart in the `parse-date-time` skill.
304
-
305
- ## Locale Matrix and Runtime ICU data
306
-
307
- See [Full format API](references/format-api.md) for the supported-locale
308
- formatting matrix and how host ICU data (full vs. small-icu Node builds)
309
- affects locale output.
310
-
311
- ## Common Mistakes
312
-
313
- ### HIGH Using Intl.DateTimeFormat directly
314
-
315
- Wrong:
316
-
317
- ```ts
318
- const formatted = new Intl.DateTimeFormat("en-US").format(new Date("2024-03-15"));
319
- ```
320
-
321
- Correct:
322
-
323
- ```ts
324
- import { formatDate } from "@northguild/gmt";
325
-
326
- const formatted = formatDate("2024-03-15", "en-US");
327
- ```
328
-
329
- Source: AGENTS.md — Never use JavaScript Date APIs
330
-
331
- ### MEDIUM Not handling empty string on invalid input
332
-
333
- Wrong:
334
-
335
- ```ts
336
- const formatted = formatDate("invalid-date");
337
- // Assume formatted is always valid string
338
- display(formatted);
339
- ```
340
-
341
- Correct:
342
-
343
- ```ts
344
- import { formatDate, isValidDate } from "@northguild/gmt";
345
-
346
- const input = "invalid-date";
347
- if (!isValidDate(input)) {
348
- display("Invalid date");
349
- } else {
350
- display(formatDate(input));
351
- }
352
- ```
353
-
354
- Source: packages/gmt/src/plain/format/formatDate.ts — Returns "" on invalid input
355
-
356
- ### MEDIUM Assuming getLocale* throws or returns English for an invalid locale
357
-
358
- Wrong:
359
-
360
- ```ts
361
- const names = getLocaleMonthNames("not-a-locale");
362
- // Assume names is non-empty or throws
363
- render(names);
364
- ```
365
-
366
- Correct:
367
-
368
- ```ts
369
- import { getLocaleEraNames, getLocaleMonthNames } from "@northguild/gmt";
370
-
371
- const names = getLocaleMonthNames("en-US");
372
- const eras = getLocaleEraNames("en-US");
373
- if (names.length === 0 || eras.length === 0) {
374
- // invalid BCP 47 tag — fall back or surface an error
375
- }
376
- ```
377
-
378
- Source: packages/gmt/src/plain/locale/getLocaleMonthNames.ts — Returns [] for an invalid locale
379
-
380
- ### MEDIUM Looking for a token-pattern formatter (there isn't one, deliberately)
381
-
382
- GMT has `parseDateWithPattern`/`parseDateTimeWithPattern`/`parseTimeWithPattern`
383
- for *decoding* a string against a caller-supplied token pattern (e.g.
384
- `"MM/dd/yyyy"`), but deliberately has no inverse token *formatter* for
385
- producing output. This is not a gap to work around — it's roadmap Decision 1
386
- (`context/roadmap/issues/J.md`): a hard-coded token pattern like
387
- `"MM/dd/yyyy"` bakes in US field ordering (month before day) and ships it to
388
- every locale, which is exactly the kind of locale bug GMT exists to prevent.
389
- Parsing a *known, fixed* producer format is fine because there's no ambiguity
390
- about what the input shape is; formatting *for output* by a fixed pattern
391
- would impose that same fixed shape on every locale's display.
392
-
393
- Wrong:
394
-
395
- ```ts
396
- // There is no formatDateWithPattern — don't go looking for one, and don't
397
- // hand-roll one by string-splitting/concatenating a formatted value into a
398
- // fixed field order.
399
- ```
400
-
401
- Correct:
402
-
403
- ```ts
404
- import { formatDate } from "@northguild/gmt";
405
-
406
- // Let Intl.DateTimeFormat pick field order per locale instead of a fixed
407
- // pattern string.
408
- formatDate("2024-03-15", "en-US"); // "3/15/2024"
409
- formatDate("2024-03-15", "de-DE"); // "15.3.2024"
410
- ```
411
-
412
- `formatDate`/`formatTime`/`formatDateTime` (locale-ordered strings) are the
413
- current answer for display. For field-level output control (a custom layout
414
- that still needs per-locale field order), use `formatDateToParts` /
415
- `formatDateTimeToParts` / `formatZonedToParts` — GMT's substitute for a
416
- token formatter (Story J12).
417
-
418
- Source: context/roadmap/issues/J.md — Decision 1 (token formatter deliberately excluded); packages/gmt/src/plain/parse/parseDateWithPattern.ts — the parsing-only counterpart
419
-
420
- ### MEDIUM Reassembling `formatToParts` output in a fixed order
421
-
422
- Wrong:
423
-
424
- ```ts
425
- import { formatDateToParts } from "@northguild/gmt";
426
-
427
- const parts = formatDateToParts("2024-03-15", locale);
428
- const month = parts.find((p) => p.type === "month")?.value;
429
- const day = parts.find((p) => p.type === "day")?.value;
430
- const year = parts.find((p) => p.type === "year")?.value;
431
- // Hard-codes US field order onto every locale — exactly the bug
432
- // formatToParts exists to avoid.
433
- const display = `${month}/${day}/${year}`;
434
- ```
435
-
436
- Correct:
437
-
438
- ```ts
439
- import { formatDateToParts } from "@northguild/gmt";
440
-
441
- const parts = formatDateToParts("2024-03-15", locale);
442
- const display = parts.map((p) => p.value).join("");
443
- // Iterate as returned — the locale already ordered the parts. Restyle
444
- // individual part values (e.g. wrap { type: "literal" } parts differently)
445
- // without changing their relative order.
446
- ```
447
-
448
- Source: context/roadmap/issues/J12 — "iterate the array as returned; reassembling parts in a fixed order reintroduces exactly the bug formatToParts exists to avoid"
449
-
450
- ### MEDIUM Confusing formatCalendar with formatRelativeDateTime
451
-
452
- They answer different questions and are not interchangeable:
453
-
454
- - `formatCalendar` (and its `formatCalendarZoned`/`formatCalendarUnix`/
455
- `formatCalendarUtc` siblings) — "what should I show on a schedule/agenda
456
- item?" → `"Tomorrow at 2:30 PM"`. Always includes the clock time; switches
457
- to an absolute date beyond ±6 days.
458
- - `formatRelativeDateTime` (and its `formatRelative*` siblings) — "how long
459
- ago/from now was this?" → `"in 1 day"`. Never includes a clock time;
460
- auto-picks the coarsest sensible unit (seconds through years) with no
461
- distance threshold.
462
-
463
- Wrong:
464
-
465
- ```ts
466
- import { formatRelativeDateTime } from "@northguild/gmt";
467
-
468
- // Building a meeting-list row and reaching for the relative formatter —
469
- // it has no time-of-day in its output at all.
470
- const row = formatRelativeDateTime(meeting.startsAt); // "in 1 day"
471
- ```
472
-
473
- Correct:
474
-
475
- ```ts
476
- import { formatCalendar } from "@northguild/gmt";
477
-
478
- const row = formatCalendar(meeting.startsAt); // "tomorrow at 2:30 PM"
479
- ```
480
-
481
- Source: context/roadmap/issues/J.md — J15's Common Mistakes requirement; packages/gmt/src/plain/format/formatCalendar.ts
482
-
483
- ## References
484
-
485
- - [Full format API](references/format-api.md)
486
- - [Intl.DateTimeFormat documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
@@ -1,34 +0,0 @@
1
- # Format API Reference
2
-
3
- ## Locale Matrix
4
-
5
- Supported locales for formatting:
6
-
7
- | Locale | Date Format | Time Format |
8
- |--------|------------|-------------|
9
- | en-US | 3/15/2024 | 2:30:45 PM |
10
- | en-GB | 15/03/2024 | 14:30:45 |
11
- | de-DE | 15.3.2024 | 14:30:45 |
12
- | fr-FR | 15/03/2024 | 14:30:45 |
13
- | es-ES | 15/3/2024 | 14:30:45 |
14
- | it-IT | 15/03/2024 | 14:30:45 |
15
- | pt-PT | 15/03/2024 | 14:30:45 |
16
- | sv-SE | 2024-03-15 | 14:30:45 |
17
- | is-IS | 15.3.2024 | 14:30:45 |
18
- | zh-CN | 2024/3/15 | 下午2:30:45 |
19
- | zh-TW | 2024/3/15 | 下午2:30:45 |
20
- | ja-JP | 2024/3/15 | 14:30:45 |
21
- | ko-KR | 2024. 3. 15. | 오후 2:30:45 |
22
- | ar-SA | ١٥‏/٣‏/٢٠٢٤ | ٢:٣٠:٤٥ م |
23
- | he-IL | 15/03/2024 | 14:30:45 |
24
- | ru-RU | 15.03.2024 | 14:30:45 |
25
- | tr-TR | 15.03.2024 | 14:30:45 |
26
-
27
- ## Runtime ICU data
28
-
29
- These formatters delegate locale rendering to the host runtime's `Intl.DateTimeFormat` / `Intl.RelativeTimeFormat`. Output therefore depends on the ICU data shipped with the running Node (or browser):
30
-
31
- - **Full ICU** runtimes (official Node binaries from nodejs.org, all modern browsers) return fully localized strings — e.g. `formatTime("14:30:00", "ko-KR", { timeStyle: "short" })` returns `"오후 2:30"`.
32
- - **Small/partial ICU** runtimes (some Node builds compiled with `--with-intl=small-icu` or repackaged distributions) fall back to English day periods and other locale data — the same call may return `"PM 2:30"`.
33
-
34
- This is a property of the runtime, not gmt. For consistent non-English output, deploy on a full-ICU Node build or polyfill `Intl` with a package that bundles locale data.