@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,412 +0,0 @@
1
- ---
2
- name: compare-dates
3
- description: >
4
- Compare date values for ordering. Use isAfterDate, isBeforeDate, areDatesEqual
5
- for comparisons. Use areDatesEqualBy/areDateTimesEqualBy/areZonedEqualBy/
6
- areUnixEqualBy/areUtcEqualBy to compare two values at a given calendar unit
7
- ("same month?"). Use isWeekend/isZonedWeekend for locale-aware weekend checks
8
- (weekend days vary by locale). Use isBusinessDay for fixed ISO Mon-Fri checks
9
- (locale-agnostic, matches addBusinessDays). Use
10
- getLocaleDayOfWeek/getLocaleZonedDayOfWeek for a locale-relative day-of-week
11
- index (0 = first day of week). Use isRelativeDay/isThisUnit/isPast/isFuture
12
- (plus isZoned* variants) for now-relative predicates ("is this today", "is
13
- this overdue") — depend on system clock/timeZone unless using the zoned
14
- variants with an explicit timeZone. Use nextWeekday/previousWeekday for the
15
- next/previous occurrence of an ISO day of week ("next Friday"). Returns
16
- false/null/"" on invalid input.
17
- sources:
18
- - 'northguild/gmt:packages/gmt/src/plain/compare/index.ts'
19
- - 'northguild/gmt:packages/gmt/src/zoned/compare/index.ts'
20
- - 'northguild/gmt:packages/gmt/src/unix/compare/index.ts'
21
- - 'northguild/gmt:packages/gmt/src/utc/compare/index.ts'
22
- - 'northguild/gmt:packages/gmt/src/plain/calculate/getLocaleDayOfWeek.ts'
23
- - 'northguild/gmt:packages/gmt/src/zoned/calculate/getLocaleZonedDayOfWeek.ts'
24
- - 'northguild/gmt:packages/gmt/src/plain/calculate/nextWeekday.ts'
25
- - 'northguild/gmt:packages/gmt/src/plain/calculate/previousWeekday.ts'
26
- metadata:
27
- type: core
28
- library: '@northguild/gmt'
29
- library_version: '1.14.2'
30
- ---
31
-
32
- # Compare Dates
33
-
34
- Use this skill when you need to compare date values for ordering.
35
-
36
- ## Setup
37
-
38
- ```ts
39
- import { isAfterDate, isBeforeDate, areDatesEqual } from "@northguild/gmt";
40
- import { isBetweenDate } from "@northguild/gmt";
41
- ```
42
-
43
- ## Core Patterns
44
-
45
- ### Check if date is after another
46
-
47
- ```ts
48
- const result = isAfterDate("2024-03-20", "2024-03-15"); // true
49
- ```
50
-
51
- ### Check if date is before another
52
-
53
- ```ts
54
- const result = isBeforeDate("2024-03-10", "2024-03-15"); // true
55
- ```
56
-
57
- ### Check if dates are equal
58
-
59
- ```ts
60
- import { areDatesEqual } from "@northguild/gmt";
61
-
62
- const result = areDatesEqual("2024-03-15", "2024-03-15"); // true
63
- ```
64
-
65
- ### Check if two values are equal at a given unit (same month, same year, ...)
66
-
67
- ```ts
68
- import { areDatesEqualBy } from "@northguild/gmt";
69
-
70
- areDatesEqualBy("2024-03-15", "2024-03-20", "month"); // true
71
- areDatesEqualBy("2023-03-15", "2024-03-15", "month"); // false — same month, different year
72
- areDatesEqualBy("2024-03-11", "2024-03-17", "week"); // true (default weekStartsOn: "monday")
73
- ```
74
-
75
- `areDatesEqualBy`/`areDateTimesEqualBy`/`areZonedEqualBy`/`areUnixEqualBy`/`areUtcEqualBy` are the namespace variants of this pattern (Decision 5 — one parameterized function per namespace instead of a `isSameDay`/`isSameMonth`/`isSameYear`/... family). Equality is measured by comparing the *start of that unit* for each value, so a coarser unit like `"month"` implicitly requires every unit above it (year) to match too — see the Common Mistakes entry below. `areZonedEqualBy` compares each value's own local calendar fields (its own time zone), not the underlying instant.
76
-
77
- Migrating from date-fns:
78
-
79
- | date-fns | GMT |
80
- | ----------------------------- | ------------------------------------- |
81
- | `isSameDay(a, b)` | `areDatesEqualBy(a, b, "day")` |
82
- | `isSameWeek(a, b, opts)` | `areDatesEqualBy(a, b, "week", opts)` |
83
- | `isSameMonth(a, b)` | `areDatesEqualBy(a, b, "month")` |
84
- | `isSameYear(a, b)` | `areDatesEqualBy(a, b, "year")` |
85
- | `isSameHour(a, b)` | `areDateTimesEqualBy(a, b, "hour")` |
86
- | `isSameMinute(a, b)` | `areDateTimesEqualBy(a, b, "minute")` |
87
- | `isSameSecond(a, b)` | `areDateTimesEqualBy(a, b, "second")` |
88
-
89
- ### Check if date is between two dates
90
-
91
- ```ts
92
- import { isBetweenDate } from "@northguild/gmt";
93
-
94
- const result = isBetweenDate("2024-03-15", "2024-03-10", "2024-03-20"); // true
95
- ```
96
-
97
- ### Compare datetime values
98
-
99
- ```ts
100
- import { isAfterDateTime, isBeforeDateTime, areDateTimesEqual } from "@northguild/gmt";
101
-
102
- const after = isAfterDateTime("2024-03-15T14:30:45", "2024-03-15T14:30:00"); // true
103
- const before = isBeforeDateTime("2024-03-15T14:30:00", "2024-03-15T14:30:45"); // true
104
- const equal = areDateTimesEqual("2024-03-15T14:30:45", "2024-03-15T14:30:45"); // true
105
- ```
106
-
107
- ### Compare time values
108
-
109
- ```ts
110
- import { isAfterTime, isBeforeTime, areTimesEqual } from "@northguild/gmt";
111
-
112
- const after = isAfterTime("14:30:45", "14:30:00"); // true
113
- const before = isBeforeTime("14:30:00", "14:30:45"); // true
114
- const equal = areTimesEqual("14:30:45", "14:30:45"); // true
115
- ```
116
-
117
- ### Check if a date falls on a weekend (locale-aware)
118
-
119
- ```ts
120
- import { isWeekend, isZonedWeekend } from "@northguild/gmt";
121
-
122
- isWeekend("2024-02-03", "en-US"); // true (Saturday, en-US weekend is Sat/Sun)
123
- isWeekend("2024-02-02", "he-IL"); // true (Friday, he-IL weekend is Fri/Sat)
124
-
125
- isZonedWeekend("2024-02-04T10:00:00+02:00[Asia/Jerusalem]", "he-IL"); // false (Sunday isn't part of he-IL's weekend)
126
- ```
127
-
128
- `isZonedWeekend` checks the `ZonedDateTime`'s own local calendar day — no separate timezone conversion needed.
129
-
130
- ### Check if a date is a business day (fixed Mon–Fri)
131
-
132
- ```ts
133
- import { isBusinessDay } from "@northguild/gmt";
134
-
135
- isBusinessDay("2024-02-05"); // true (Monday)
136
- isBusinessDay("2024-02-10"); // false (Saturday)
137
- isBusinessDay("2024-02-04"); // false (Sunday)
138
- ```
139
-
140
- `isBusinessDay` uses the fixed ISO Monday–Friday boundary (Mon=1 … Fri=5) — locale-agnostic and with no holiday calendar. It matches the boundary that `addBusinessDays`/`subtractBusinessDays` use, and is the locale-agnostic complement to `isWeekend` (which resolves weekend days per locale via `Intl.Locale`'s `weekInfo`).
141
-
142
- ### Get the locale-relative day-of-week index
143
-
144
- ```ts
145
- import { getLocaleDayOfWeek, getLocaleZonedDayOfWeek } from "@northguild/gmt";
146
-
147
- getLocaleDayOfWeek("2024-02-25", "en-US"); // 0 (Sunday = first day of en-US week)
148
- getLocaleDayOfWeek("2024-02-26", "en-US"); // 1 (Monday)
149
- getLocaleDayOfWeek("2024-02-26", "fr-FR"); // 0 (Monday = first day of fr-FR week)
150
- getLocaleDayOfWeek("2024-02-24", "he-IL"); // 0 (Saturday = first day of he-IL week)
151
-
152
- getLocaleZonedDayOfWeek("2024-02-25T12:00:00+00:00[UTC]", "en-US"); // 0
153
- getLocaleZonedDayOfWeek("2024-02-26T12:00:00+00:00[UTC]", "fr-FR"); // 0
154
- ```
155
-
156
- `getLocaleDayOfWeek` returns `null` on invalid input. `getLocaleZonedDayOfWeek` reads the `ZonedDateTime`'s local calendar day — no separate timezone conversion needed.
157
-
158
- ### Check now-relative predicates (today, this month, past, future)
159
-
160
- ```ts
161
- import { isRelativeDay, isThisUnit, isPast, isFuture } from "@northguild/gmt";
162
-
163
- isRelativeDay("2024-03-15", 0); // "is today" — true if today is 2024-03-15
164
- isRelativeDay("2024-03-14", -1); // "is yesterday" — true if today is 2024-03-15
165
- isRelativeDay("2024-03-16", 1); // "is tomorrow" — true if today is 2024-03-15
166
-
167
- isThisUnit("2024-03-15", "month"); // true if today is any day in March 2024
168
- isThisUnit("2024-02-26", "week", "fr-FR"); // locale-aware week boundary (fr-FR starts Monday)
169
-
170
- isPast("2024-03-14"); // true if today is 2024-03-15 (strictly before, not on-or-before)
171
- isFuture("2024-03-16"); // true if today is 2024-03-15 (strictly after, not on-or-before)
172
- ```
173
-
174
- `isRelativeDay` subsumes `isToday`/`isYesterday`/`isTomorrow` (`offsetDays: 0`/`-1`/`1`); `isThisUnit` subsumes `isThisWeek`/`isThisMonth`/`isThisYear` (Decision 5, `context/roadmap/issues/J.md`). All four compare against `getToday()`, so they depend on the **system clock and system timeZone** — see the Common Mistakes entry below before using these in a server or test context.
175
-
176
- Zoned counterparts — `isZonedRelativeDay`, `isZonedThisUnit`, `isZonedPast`, `isZonedFuture` — take a `ZonedDateTime` string and resolve "today"/"now" in *that value's own* timeZone, making them deterministic regardless of the host's system timeZone:
177
-
178
- ```ts
179
- import { isZonedRelativeDay, isZonedThisUnit, isZonedPast, isZonedFuture } from "@northguild/gmt";
180
-
181
- isZonedRelativeDay("2024-03-15T10:00:00-04:00[America/New_York]", 0); // "today" in America/New_York, not the host's timeZone
182
- isZonedThisUnit("2024-03-15T10:00:00-04:00[America/New_York]", "month");
183
-
184
- // isZonedPast/isZonedFuture compare the exact instant (not just the calendar
185
- // day) against Temporal.Now.instant() — the zoned counterpart carries a
186
- // full time-of-day, unlike the plain, day-only isPast/isFuture.
187
- isZonedPast("2020-01-01T00:00:00Z[UTC]"); // true
188
- isZonedFuture("2999-01-01T00:00:00Z[UTC]"); // true
189
- ```
190
-
191
- ### Find the next/previous occurrence of a weekday
192
-
193
- ```ts
194
- import { nextWeekday, previousWeekday } from "@northguild/gmt";
195
-
196
- nextWeekday("2024-03-13", 5); // "2024-03-15" (Wednesday -> next Friday)
197
- nextWeekday("2024-03-15", 5); // "2024-03-22" (already Friday -> advances a full week by default)
198
- nextWeekday("2024-03-15", 5, { inclusive: true }); // "2024-03-15" (already Friday -> returned as-is)
199
-
200
- previousWeekday("2024-03-13", 5); // "2024-03-08" (Wednesday -> previous Friday)
201
- previousWeekday("2024-03-15", 5); // "2024-03-08" (already Friday -> goes back a full week by default)
202
- ```
203
-
204
- `dayOfWeek` uses Temporal's ISO numbering (1 = Monday … 7 = Sunday), the same as `getDayOfWeek`/`parseDayOfWeekFromDate`. These replace date-fns's sixteen `next*`/`previous*` functions with two parameterized calls (Decision 5, `context/roadmap/issues/J.md`):
205
-
206
- | date-fns | GMT |
207
- | ------------------ | ------------------------------ |
208
- | `nextMonday(v)` | `nextWeekday(v, 1)` |
209
- | `nextFriday(v)` | `nextWeekday(v, 5)` |
210
- | `nextDay(v, n)` | `nextWeekday(v, n)` |
211
- | `previousFriday(v)`| `previousWeekday(v, 5)` |
212
- | `previousDay(v, n)`| `previousWeekday(v, n)` |
213
-
214
- `date-fns`'s `lastDayOfMonth` is already covered by `endOfDate(value, "month")` — not a gap this pair fills.
215
-
216
- ## Common Mistakes
217
-
218
- ### HIGH Using string comparison for dates
219
-
220
- Wrong:
221
-
222
- ```ts
223
- const isAfter = "2024-03-20" > "2024-03-15"; // true but fragile
224
- ```
225
-
226
- Correct:
227
-
228
- ```ts
229
- import { isAfterDate } from "@northguild/gmt";
230
-
231
- const isAfter = isAfterDate("2024-03-20", "2024-03-15"); // true
232
- ```
233
-
234
- Source: AGENTS.md — Use Temporal for proper comparison
235
-
236
- ### MEDIUM Not handling invalid input
237
-
238
- Wrong:
239
-
240
- ```ts
241
- const result = isAfterDate("invalid", "2024-03-15");
242
- // Assume result is always boolean
243
- if (result) { // false, not throwing
244
- process();
245
- }
246
- ```
247
-
248
- Correct:
249
-
250
- ```ts
251
- import { isAfterDate, isValidDate } from "@northguild/gmt";
252
-
253
- const dateA = "invalid";
254
- const dateB = "2024-03-15";
255
- if (!isValidDate(dateA) || !isValidDate(dateB)) {
256
- throw new Error("Invalid date");
257
- }
258
- const result = isAfterDate(dateA, dateB);
259
- ```
260
-
261
- Source: packages/gmt/src/plain/compare/isAfterDate.ts — Returns false on invalid
262
-
263
- ### MEDIUM Comparing different date formats
264
-
265
- Wrong:
266
-
267
- ```ts
268
- const result = isAfterDate("2024-3-5", "2024-03-15"); // may be incorrect
269
- ```
270
-
271
- Correct:
272
-
273
- ```ts
274
- // Ensure canonical format before comparison
275
- import { isAfterDate } from "@northguild/gmt";
276
-
277
- const result = isAfterDate("2024-03-05", "2024-03-15");
278
- ```
279
-
280
- Source: Temporal.PlainDate.from() — canonicalizes input
281
-
282
- ### MEDIUM Assuming weekends are always Saturday/Sunday
283
-
284
- Wrong:
285
-
286
- ```ts
287
- import { getDayOfWeek } from "@northguild/gmt";
288
-
289
- const day = getDayOfWeek(); // ISO day of week, always Monday-start
290
- const isWeekendDay = day === 6 || day === 7; // wrong for he-IL/ar-SA (Fri/Sat)
291
- ```
292
-
293
- Correct:
294
-
295
- ```ts
296
- import { isWeekend } from "@northguild/gmt";
297
-
298
- const isWeekendDay = isWeekend("2024-02-02", "he-IL"); // true — Friday is part of he-IL's weekend
299
- ```
300
-
301
- Most locales use Saturday/Sunday, but `he-IL`/`ar-SA` use Friday/Saturday — `isWeekend`/`isZonedWeekend` resolve this per-locale via `Intl.Locale`'s `weekInfo` instead of hardcoding a day pair.
302
-
303
- Source: packages/gmt/src/plain/compare/isWeekend.ts — locale-aware via Intl.Locale weekInfo
304
-
305
- ### MEDIUM Confusing isBusinessDay with isWeekend
306
-
307
- Wrong:
308
-
309
- ```ts
310
- const isWeekendDay = isBusinessDay("2024-02-02"); // false (it's a Friday)
311
- // then assume isBusinessDay captures locale-specific weekend days
312
- ```
313
-
314
- Correct:
315
-
316
- ```ts
317
- import { isWeekend } from "@northguild/gmt";
318
-
319
- // isBusinessDay is fixed ISO Mon–Fri (no locale lookup, no holidays)
320
- isBusinessDay("2024-02-02"); // true (Friday)
321
-
322
- // For locale-aware weekend detection (e.g. he-IL Fri/Sat), use isWeekend
323
- isWeekend("2024-02-02", "he-IL"); // true (Friday is part of he-IL's weekend)
324
- ```
325
-
326
- `isBusinessDay` uses a fixed Monday–Friday boundary and never consults `Intl.Locale`'s `weekInfo`; it does not account for holidays. Do not reach for it when you need locale-aware weekend detection — that's `isWeekend`/`isZonedWeekend`'s job.
327
-
328
- Source: packages/gmt/src/plain/compare/isBusinessDay.ts — fixed ISO Mon–Fri, locale-agnostic
329
-
330
- ### MEDIUM Assuming "same month" ignores the year
331
-
332
- Wrong:
333
-
334
- ```ts
335
- import { areDatesEqualBy } from "@northguild/gmt";
336
-
337
- // Assuming this checks "is it March, regardless of year"
338
- const bothMarch = areDatesEqualBy("2023-03-15", "2024-03-15", "month"); // false, not true
339
- ```
340
-
341
- Correct:
342
-
343
- ```ts
344
- import { areDatesEqualBy } from "@northguild/gmt";
345
-
346
- // "Same month" means the same month AND year — matches date-fns's isSameMonth
347
- // and Luxon's dt.hasSame(other, "month")
348
- areDatesEqualBy("2024-03-01", "2024-03-31", "month"); // true (same month, same year)
349
-
350
- // To check "same month-of-year regardless of year", compare the month field directly
351
- import { parseMonthFromDate } from "@northguild/gmt";
352
- const sameMonthOfYear =
353
- parseMonthFromDate("2023-03-15") === parseMonthFromDate("2024-03-15"); // true
354
- ```
355
-
356
- `areDatesEqualBy(a, b, unit)` compares the start-of-`unit` boundary for each value, so any unit implicitly requires every coarser unit above it to match too — this is deliberate (Decision 5/6, `context/roadmap/issues/J.md`) and matches date-fns/Luxon precedent, not a GMT-specific quirk.
357
-
358
- Source: packages/gmt/src/plain/compare/areDatesEqualBy.ts — start-of-unit comparison
359
-
360
- ### HIGH Assuming isRelativeDay/isThisUnit/isPast/isFuture are timeZone-independent
361
-
362
- Wrong:
363
-
364
- ```ts
365
- import { isRelativeDay } from "@northguild/gmt";
366
-
367
- // Assuming "today" means the same thing everywhere
368
- const isDueToday = isRelativeDay(dueDate, 0);
369
- ```
370
-
371
- Correct:
372
-
373
- ```ts
374
- import { isZonedRelativeDay } from "@northguild/gmt";
375
-
376
- // "Today" is resolved in the value's own timeZone — deterministic
377
- // regardless of the host machine's system timeZone
378
- const isDueToday = isZonedRelativeDay(dueZonedDateTime, 0);
379
- ```
380
-
381
- `isRelativeDay`/`isThisUnit`/`isPast`/`isFuture` depend on the **system clock and system timeZone** (they compare against `getToday()`). The same call returns different answers on hosts in different timeZones at the same instant — `isRelativeDay("2024-03-15", 0)` can be true in one timeZone and false in another 24 hours apart (e.g. `Pacific/Apia` vs. `Pacific/Niue`). Callers needing determinism — server-side rendering, tests, scheduled jobs — should use the zoned variants (`isZonedRelativeDay`, `isZonedThisUnit`, `isZonedPast`, `isZonedFuture`) with an explicit timeZone, or compare against an explicit reference with `areDatesEqualBy`/`isBeforeDate`/`isAfterDate`.
382
-
383
- Source: packages/gmt/src/plain/compare/isRelativeDay.ts, packages/gmt/src/plain/get/getToday.ts — system clock/timeZone dependency
384
-
385
- ### MEDIUM Assuming nextWeekday/previousWeekday return the input when it's already on the target day
386
-
387
- Wrong:
388
-
389
- ```ts
390
- import { nextWeekday } from "@northguild/gmt";
391
-
392
- // Assuming a Friday input returns itself when asking for "next Friday"
393
- const result = nextWeekday("2024-03-15", 5); // "2024-03-22", not "2024-03-15"
394
- ```
395
-
396
- Correct:
397
-
398
- ```ts
399
- import { nextWeekday } from "@northguild/gmt";
400
-
401
- // Pass { inclusive: true } to return the input as-is when it already falls on dayOfWeek
402
- const result = nextWeekday("2024-03-15", 5, { inclusive: true }); // "2024-03-15"
403
- ```
404
-
405
- `options.inclusive` defaults to `false`, matching date-fns: a `value` already on `dayOfWeek` advances a full week rather than returning itself. Same default applies to `previousWeekday`.
406
-
407
- Source: packages/gmt/src/plain/calculate/nextWeekday.ts, packages/gmt/src/plain/calculate/previousWeekday.ts — inclusive defaults to false
408
-
409
- ## References
410
-
411
- - [Full compare API](references/compare-api.md)
412
- - [Temporal.PlainDate comparison](https://tc39.es/proposal-temporal/docs/plaindate.html#comparison)