@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,349 +0,0 @@
1
- ---
2
- name: parse-date-time
3
- description: >
4
- Parse individual components (year, month, day, hour, etc.) from date/time
5
- strings. Use parse* functions for extraction. Returns null on invalid input.
6
- Also parseRfc2822, parseHttp, parseSql, parseRfc3339 for decoding fixed
7
- interchange grammars (email Date headers, HTTP headers, SQL datetime literals,
8
- RFC 3339) into GMT's own ISO strings — these return "" on invalid input, not
9
- null.
10
- sources:
11
- - 'northguild/gmt:packages/gmt/src/plain/parse/index.ts'
12
- - 'northguild/gmt:packages/gmt/src/zoned/parse/parseRfc2822.ts'
13
- - 'northguild/gmt:packages/gmt/src/utc/parse/parseHttp.ts'
14
- - 'northguild/gmt:packages/gmt/src/plain/parse/parseSql.ts'
15
- - 'northguild/gmt:packages/gmt/src/zoned/parse/parseRfc3339.ts'
16
- metadata:
17
- type: core
18
- library: '@northguild/gmt'
19
- library_version: '1.14.2'
20
- ---
21
-
22
- # Parse Date/Time Components
23
-
24
- Use this skill when you need to extract a specific component (year, month, day, etc.) from a date or time string.
25
-
26
- ## Setup
27
-
28
- ```ts
29
- import { parseYearFromDate, parseMonthFromDate, parseDayFromDate } from "@northguild/gmt";
30
- import { parseHourFromTime, parseMinuteFromTime, parseSecondFromTime } from "@northguild/gmt";
31
- ```
32
-
33
- ## Core Patterns
34
-
35
- ### Parse year from date
36
-
37
- ```ts
38
- const year = parseYearFromDate("2024-03-15"); // 2024
39
- ```
40
-
41
- ### Parse month from date
42
-
43
- ```ts
44
- const month = parseMonthFromDate("2024-03-15"); // 3
45
- ```
46
-
47
- ### Parse day from date
48
-
49
- ```ts
50
- const day = parseDayFromDate("2024-03-15"); // 15
51
- ```
52
-
53
- ### Parse hour from time
54
-
55
- ```ts
56
- const hour = parseHourFromTime("14:30:45"); // 14
57
- ```
58
-
59
- ### Parse minute from time
60
-
61
- ```ts
62
- const minute = parseMinuteFromTime("14:30:45"); // 30
63
- ```
64
-
65
- ### Parse second from time
66
-
67
- ```ts
68
- const second = parseSecondFromTime("14:30:45"); // 45
69
- ```
70
-
71
- ### Parse from datetime string
72
-
73
- ```ts
74
- import { parseYearFromDateTime, parseMonthFromDateTime, parseDayFromDateTime } from "@northguild/gmt";
75
-
76
- const year = parseYearFromDateTime("2024-03-15T14:30:45"); // 2024
77
- const month = parseMonthFromDateTime("2024-03-15T14:30:45"); // 3
78
- const day = parseDayFromDateTime("2024-03-15T14:30:45"); // 15
79
- ```
80
-
81
- ### Parse day of week
82
-
83
- ```ts
84
- import { parseDayOfWeekFromDate } from "@northguild/gmt";
85
-
86
- const dayOfWeek = parseDayOfWeekFromDate("2024-03-15"); // 5 (Friday)
87
- ```
88
-
89
- ### Parse week of year
90
-
91
- ```ts
92
- import { parseWeekFromDate } from "@northguild/gmt";
93
-
94
- const week = parseWeekFromDate("2024-03-15"); // 11
95
- ```
96
-
97
- ### Get calendar quantities (days in month/year, day of year, week counts)
98
-
99
- These aren't `parse*` functions — they don't extract a stored field, they compute
100
- a derived quantity — so they live in `calculate/`, not `parse/`. Grouped here
101
- because they answer the same "read a calendar fact off a date" need.
102
-
103
- ```ts
104
- import {
105
- getDaysInMonth,
106
- getDaysInYear,
107
- getDayOfYear,
108
- getWeeksInYear,
109
- getWeeksInMonth,
110
- getWeekOfMonth,
111
- getWeekYear,
112
- getLocaleWeekYear,
113
- getWeeksInLocaleWeekYear,
114
- } from "@northguild/gmt";
115
-
116
- getDaysInMonth("2024-02-15"); // 29 (leap year)
117
- getDaysInYear("2024-06-15"); // 366 (leap year)
118
- getDayOfYear("2024-03-01"); // 61
119
- getWeeksInYear("2024-06-15"); // 52 (ISO week-numbering year)
120
- getWeeksInMonth("2024-02-15", "en-US"); // 5 (calendar-grid rows, locale week start)
121
- getWeekOfMonth("2024-02-29", "en-US"); // 5 (which grid row this date falls on)
122
- getWeekYear("2024-12-30"); // 2025 (ISO week-numbering year — a Monday in week 1 of 2025)
123
- getLocaleWeekYear("2022-01-01", "en-US"); // 2022 (locale-relative: en-US always counts Jan 1 as week 1)
124
- getWeeksInLocaleWeekYear("2020-06-15", "de-DE"); // 53
125
- ```
126
-
127
- ### Parse a fixed producer format against a token pattern
128
-
129
- Unlike the `parse*From*` functions above, these decode a whole string against a
130
- caller-supplied token pattern instead of pulling one field out of an ISO
131
- string — use them for a known, fixed non-ISO producer format (a CSV column, a
132
- legacy API field, a partially-typed form value).
133
-
134
- ```ts
135
- import {
136
- parseDateWithPattern,
137
- parseDateTimeWithPattern,
138
- parseTimeWithPattern,
139
- } from "@northguild/gmt";
140
-
141
- parseDateWithPattern("03/15/2024", "MM/dd/yyyy"); // "2024-03-15"
142
- parseDateWithPattern("15-Mar-2024", "dd-MMM-yyyy"); // "2024-03-15"
143
- parseTimeWithPattern("02:30:45 PM", "hh:mm:ss a"); // "14:30:45"
144
- parseDateTimeWithPattern("15-Mar-2024 2:30 PM", "dd-MMM-yyyy h:mm a"); // "2024-03-15T14:30:00"
145
- parseDateWithPattern("02/31/2024", "MM/dd/yyyy"); // "" — shape-valid, not a real date
146
- ```
147
-
148
- Token table (subset — see JSDoc on each function for the full table):
149
-
150
- | Token | Field | Notes |
151
- | ----------------- | --------------------- | --------------------------------------------------- |
152
- | `yyyy` / `yy` | year | `yy` pivots 00–68 → 2000s, 69–99 → 1900s |
153
- | `MM` / `M` | month (numeric) | |
154
- | `MMMM` / `MMM` | month name | locale-aware, via `getLocaleMonthNames` |
155
- | `dd` / `d` | day | |
156
- | `EEEE` / `EEE` | weekday name | locale-aware; consumed but not cross-validated |
157
- | `HH` / `H` | hour (24h) | |
158
- | `hh` / `h` | hour (12h) | needs `a` in the pattern to resolve to 24h |
159
- | `mm` / `m` | minute | |
160
- | `ss` / `s` | second | |
161
- | `SSS` | millisecond | |
162
- | `a` | meridiem | locale-aware, via `getLocaleMeridiems` |
163
- | `GGGG` / `GG` | era name | locale-aware; BCE label ⇒ final year = 1 − parsed year |
164
-
165
- `parseDateWithPattern` rejects time-only tokens and `parseTimeWithPattern`
166
- rejects date-only tokens (both return `""`); `parseDateTimeWithPattern`
167
- accepts the combined set. All three return `""` — never `null` — on no
168
- match, a malformed pattern, or a shape-valid-but-unreal date/time (the regex
169
- only proves shape; `Temporal.*.from(..., { overflow: "reject" })` proves the
170
- value is real).
171
-
172
- Full docs: `packages/gmt/src/plain/parse/parseDateWithPattern.ts`,
173
- `parseDateTimeWithPattern.ts`, `parseTimeWithPattern.ts`.
174
-
175
- ### Parse a named machine format (email, HTTP, SQL, RFC 3339)
176
-
177
- Unlike every other function on this page, these four return `""` on invalid
178
- input (they produce a whole datetime string, not a numeric/component value),
179
- matching the sentinel convention of their `format*` counterparts in the
180
- `format-date-time` skill rather than `parse*From*`'s `null`.
181
-
182
- ```ts
183
- import { parseRfc2822, parseHttp, parseSql, parseRfc3339 } from "@northguild/gmt";
184
-
185
- // Email `Date:` headers (RFC 5322 / RFC 2822) → zoned ISO string.
186
- parseRfc2822("Fri, 15 Mar 2024 14:30:00 -0400");
187
- // "2024-03-15T14:30:00-04:00[-04:00]"
188
-
189
- // HTTP headers (RFC 7231 IMF-fixdate only — not RFC 850/asctime) → utc ISO string.
190
- parseHttp("Fri, 15 Mar 2024 14:30:00 GMT");
191
- // "2024-03-15T14:30:00Z"
192
-
193
- // ANSI SQL / ODBC datetime literal (no time zone) → plain ISO string.
194
- parseSql("2024-03-15 14:30:00");
195
- // "2024-03-15T14:30:00"
196
-
197
- // Strict RFC 3339 → zoned ISO string (offset doubles as the zone identifier,
198
- // since RFC 3339 carries no IANA zone name).
199
- parseRfc3339("2024-03-15T14:30:00-04:00");
200
- // "2024-03-15T14:30:00-04:00[-04:00]"
201
- ```
202
-
203
- `parseRfc2822` accepts a 1- or 2-digit day and RFC 5322's obsolete named
204
- zones (`GMT`, `UT`, and the eight North American zones); `parseHttp` requires
205
- strict 2-digit fields and a literal `GMT` only. Each has a `format*`
206
- counterpart in the `format-date-time` skill.
207
-
208
- ## Common Mistakes
209
-
210
- ### HIGH Not handling null on invalid input
211
-
212
- Wrong:
213
-
214
- ```ts
215
- const year = parseYearFromDate("invalid");
216
- // Assume year is always a number
217
- console.log(year + 1); // NaN
218
- ```
219
-
220
- Correct:
221
-
222
- ```ts
223
- const year = parseYearFromDate("invalid");
224
- if (year === null) {
225
- throw new Error("Invalid date");
226
- }
227
- console.log(year + 1);
228
- ```
229
-
230
- Source: packages/gmt/src/plain/parse/parseYearFromDate.ts — Returns null on invalid input
231
-
232
- ### MEDIUM Using string manipulation instead of parse functions
233
-
234
- Wrong:
235
-
236
- ```ts
237
- const year = "2024-03-15".split("-")[0]; // "2024" as string
238
- ```
239
-
240
- Correct:
241
-
242
- ```ts
243
- import { parseYearFromDate } from "@northguild/gmt";
244
-
245
- const year = parseYearFromDate("2024-03-15"); // 2024 as number
246
- ```
247
-
248
- Source: AGENTS.md — Use Temporal for date manipulation
249
-
250
- ### MEDIUM Assuming month is 1-indexed in arithmetic
251
-
252
- Wrong:
253
-
254
- ```ts
255
- const month = parseMonthFromDate("2024-01-15");
256
- // Use directly in array indexing
257
- const monthName = ["Jan", "Feb", "Mar"][month]; // undefined
258
- ```
259
-
260
- Correct:
261
-
262
- ```ts
263
- const month = parseMonthFromDate("2024-01-15");
264
- const monthName = ["Jan", "Feb", "Mar"][month - 1]; // "Jan"
265
- ```
266
-
267
- Source: Temporal spec — months are 1-indexed
268
-
269
- ### MEDIUM Confusing getWeekOfMonth with parseWeekFromDate
270
-
271
- `parseWeekFromDate` returns the ISO week-of-**year** (1-53, resets every
272
- January). `getWeekOfMonth` returns the calendar-grid row within the
273
- **month** (1-4/5/6, resets every month, and depends on `locale`'s first
274
- day of week). They answer different questions and are not interchangeable.
275
-
276
- Wrong:
277
-
278
- ```ts
279
- // Trying to size a datepicker's month grid
280
- const row = parseWeekFromDate("2024-02-29"); // 9 — ISO week-of-year, not useful here
281
- ```
282
-
283
- Correct:
284
-
285
- ```ts
286
- import { getWeekOfMonth } from "@northguild/gmt";
287
-
288
- const row = getWeekOfMonth("2024-02-29", "en-US"); // 5 — grid row within February
289
- ```
290
-
291
- Source: packages/gmt/src/plain/calculate/getWeekOfMonth.ts
292
-
293
- ### MEDIUM Week number without week-year is a bug in waiting
294
-
295
- `parseWeekFromDate`/`getWeekNumber` return the week number alone, which
296
- is ambiguous across a year boundary — 2024-12-30 is ISO week 1, but of
297
- 2025, not 2024. Always pair it with `getWeekYear` (ISO) or
298
- `getLocaleWeekYear` (locale-relative) when bucketing by week, or
299
- December dates land in the wrong bucket.
300
-
301
- Wrong:
302
-
303
- ```ts
304
- const key = parseWeekFromDate("2024-12-30"); // "1" — 1 of which year?
305
- ```
306
-
307
- Correct:
308
-
309
- ```ts
310
- import { getWeekYear, parseWeekFromDate } from "@northguild/gmt";
311
-
312
- const key = `${getWeekYear("2024-12-30")}-W${parseWeekFromDate("2024-12-30")}`; // "2025-W1"
313
- ```
314
-
315
- Source: packages/gmt/src/plain/calculate/getWeekYear.ts, getLocaleWeekYear.ts
316
-
317
- ### MEDIUM Checking for `null` on parseRfc2822/parseHttp/parseSql/parseRfc3339
318
-
319
- Unlike every `parse*From*` component extractor on this page, these four
320
- return a **string** (a whole datetime), so their sentinel is `""`, not
321
- `null` — checking for `null` silently lets an empty string through.
322
-
323
- Wrong:
324
-
325
- ```ts
326
- const value = parseHttp(headerValue);
327
- if (value === null) {
328
- throw new Error("bad Last-Modified header");
329
- }
330
- // "" passes this check and flows downstream as if valid.
331
- ```
332
-
333
- Correct:
334
-
335
- ```ts
336
- import { parseHttp } from "@northguild/gmt";
337
-
338
- const value = parseHttp(headerValue);
339
- if (value === "") {
340
- throw new Error("bad Last-Modified header");
341
- }
342
- ```
343
-
344
- Source: packages/gmt/src/utc/parse/parseHttp.ts — Returns "" on invalid input, matching GMT's string-return sentinel convention
345
-
346
- ## References
347
-
348
- - [Full parse API](references/parse-api.md)
349
- - [Temporal.PlainDate documentation](https://tc39.es/proposal-temporal/docs/plaindate.html)
@@ -1,180 +0,0 @@
1
- ---
2
- name: pr-contribution
3
- description: >
4
- Contribute improvements to @northguild/gmt via pull requests, including new
5
- methods, bug fixes, and documentation updates.
6
- metadata:
7
- library_version: 1.14.2
8
- ---
9
-
10
- # PR Contribution
11
-
12
- Use this skill when you want to contribute code directly to gmt.
13
-
14
- ## Contribution Types
15
-
16
- - **New method**: Add a missing date/time operation
17
- - **Bug fix**: Correct existing behavior
18
- - **Documentation**: Improve docs or addexamples
19
- - **Tests**: Add test coverage for missing cases
20
-
21
- ## Prerequisites
22
-
23
- 1. Install the development environment:
24
- ```bash
25
- pnpm install
26
- ```
27
-
28
- 2. Understand the project structure:
29
- ```
30
- packages/gmt/src/
31
- ├── plain/ # Timezone-free operations
32
- ├── zoned/ # Timezone-aware operations
33
- ├── utc/ # UTC operations
34
- ├── unix/ # Unix epoch operations
35
- └── regex/ # Regex patterns
36
- ```
37
-
38
- ## Creating a PR
39
-
40
- ### 1. Fork and Clone
41
-
42
- ```bash
43
- git clone https://github.com/YOUR_FORK/gmt.git
44
- cd gmt
45
- pnpm install
46
- ```
47
-
48
- ### 2. Create a Feature Branch
49
-
50
- ```bash
51
- git checkout -b feature/my-new-method
52
- ```
53
-
54
- ### 3. Implement the Method
55
-
56
- Follow the `new-method-implementation` skill guidelines:
57
-
58
- - Use Temporal under the hood
59
- - String-in/string-out APIs
60
- - Plain/zoned separation
61
- - Proper error handling
62
-
63
- **Example structure:**
64
-
65
- ```ts
66
- // packages/gmt/src/plain/get/getQuarter.ts
67
- import { Temporal } from "@js-temporal/polyfill";
68
- import { PlainDateSchema } from "../types";
69
-
70
- /**
71
- * Get the ISO quarter (1-4) for a PlainDate.
72
- *
73
- * @param dateStr ISO 8601 date string (e.g. "2024-03-15")
74
- * @returns Quarter 1-4, or null on invalid input
75
- *
76
- * @example getQuarter("2024-03-15") // 1
77
- * @example getQuarter("2024-05-20") // 2
78
- */
79
- export function getQuarter(dateStr: string): number | null {
80
- if (!PlainDateSchema.safeParse(dateStr).success) {
81
- return null;
82
- }
83
- try {
84
- const date = Temporal.PlainDate.from(dateStr);
85
- return Math.ceil(date.month / 3);
86
- } catch {
87
- return null;
88
- }
89
- }
90
- ```
91
-
92
- ### 4. Add Tests
93
-
94
- Add comprehensive tests:
95
-
96
- ```ts
97
- // packages/gmt/src/plain/get/getQuarter.test.ts
98
- import { describe, it, expect } from "vitest";
99
- import { getQuarter } from "./getQuarter";
100
-
101
- describe("getQuarter", () => {
102
- it.each`
103
- input | expected
104
- ${"2024-01-01"} | ${1}
105
- ${"2024-03-31"} | ${1}
106
- ${"2024-04-01"} | ${2}
107
- ${"2024-06-30"} | ${2}
108
- ${"2024-07-01"} | ${3}
109
- ${"2024-09-30"} | ${3}
110
- ${"2024-10-01"} | ${4}
111
- ${"2024-12-31"} | ${4}
112
- `("returns $expected for $input", ({ input, expected }) => {
113
- expect(getQuarter(input)).toBe(expected);
114
- });
115
-
116
- it("returns null for invalid input", () => {
117
- expect(getQuarter("invalid")).toBe(null);
118
- });
119
- });
120
- ```
121
-
122
- ### 5. Run Tests
123
-
124
- ```bash
125
- pnpm run test:gmt
126
- ```
127
-
128
- ### 6. Run Linting
129
-
130
- ```bash
131
- pnpm run lint
132
- ```
133
-
134
- ### 7. Commit and Push
135
-
136
- ```bash
137
- git add .
138
- git commit -m "feat(plain): add getQuarter function"
139
- git push origin feature/my-new-method
140
- ```
141
-
142
- ### 8. Open PR
143
-
144
- Use the PR template:
145
-
146
- ```markdown
147
- ## Summary
148
- Brief description of what this adds/fixes
149
-
150
- ## Changes
151
- - Added `getQuarter` to plain/get
152
-
153
- ## Testing
154
- - Added unit tests for happy, invalid, and boundary paths
155
- - All tests pass
156
-
157
- ## Checklist
158
- - [ ] Tests added
159
- - [ ] Tests pass
160
- - [ ] No Date APIs used
161
- - [ ] String-in/string-out followed
162
- ```
163
-
164
- ## PR Requirements
165
-
166
- 1. **No Date APIs**: Use Temporal only
167
- 2. **Tests required**: Happy path, invalid path, boundary cases
168
- 3. **Documentation**: JSDoc with @example tags
169
- 4. **Lint passes**: No Biome errors
170
-
171
- ## Before Opening
172
-
173
- - [ ] Tests pass: `pnpm run test:gmt`
174
- - [ ] Lint passes: `pnpm run lint`
175
- - [ ] Typecheck passes: `pnpm run typecheck`
176
-
177
- ## Related Skills
178
-
179
- - **issue-creation**: If you want to discuss before implementing
180
- - **new-method-implementation**: Implementation guidelines
@@ -1,56 +0,0 @@
1
- ---
2
- name: unit-test-generation
3
- description: >
4
- Write or update gmt unit tests with deterministic Temporal-safe patterns,
5
- invalid-input fallback assertions, locale-matrix coverage, and DST/timezone
6
- boundary cases required by project policy.
7
- metadata:
8
- library_version: 1.14.2
9
- ---
10
-
11
- # Unit Test Generation
12
-
13
- Use this skill when writing or updating tests for `packages/gmt/src/*` methods.
14
-
15
- ## Required patterns
16
-
17
- 1. Use `it.each` template literals for iterative tests.
18
- 2. Use fake timers for now-dependent behavior.
19
- 3. Never override real runtime functions directly.
20
- 4. Validate type-specific fallback behavior on invalid input.
21
- 5. For locale-aware methods, include the required locale matrix explicitly.
22
-
23
- ## Test checklist
24
-
25
- 1. Happy path
26
- - Verify expected output for representative valid inputs.
27
-
28
- 2. Invalid input path
29
- - String-returning method returns `""`.
30
- - Number-returning method returns `null`.
31
- - Boolean-returning method returns `false`.
32
-
33
- - Tests should assert the strict input contract: only ISO 8601 strings, IANA timezone identifiers, or explicit unix epoch numbers are accepted by public APIs. When inputs are outside these shapes, assert the library's typed fallbacks are returned rather than relying on permissive parsing.
34
-
35
-
36
- 3. Boundary path
37
- - Include leap day and month rollover scenarios.
38
- - For zoned behavior, include DST or offset-sensitive scenarios.
39
-
40
- 4. Locale path
41
- - For locale-aware APIs, include explicit `MustTestLocales.*` rows.
42
-
43
- ## Timer and mocking policy
44
-
45
- 1. Use `vi.useFakeTimers()` and `vi.setSystemTime(...)`.
46
- 2. Restore with `vi.useRealTimers()`.
47
- 3. Use `vi.spyOn(...).mockReturnValue(...)` style APIs when stubbing.
48
- 4. Do not monkey-patch globals or Temporal internals.
49
-
50
- ## Quality bar for new custom methods
51
-
52
- When a new method is added because no gmt method exists:
53
-
54
- 1. Add strong tests in the same PR.
55
- 2. Cover happy path, invalid path, and boundary path.
56
- 3. Prefer explicit table-driven tests over ad hoc single-case tests.