@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.
- package/README.md +43 -13
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plain/calculate/diffDateAsDuration.js +1 -1
- package/dist/plain/calculate/index.d.ts +2 -1
- package/dist/plain/calculate/index.js +2 -1
- package/dist/plain/calculate/{weekOfYear.d.ts → weekOfYearForDate.d.ts} +0 -14
- package/dist/plain/calculate/weekOfYearForDate.js +28 -0
- package/dist/plain/calculate/weekOfYearForDateTime.d.ts +14 -0
- package/dist/plain/calculate/{weekOfYear.js → weekOfYearForDateTime.js} +1 -27
- package/dist/plain/format/formatCalendar.d.ts +2 -2
- package/dist/plain/format/formatDate.d.ts +3 -2
- package/dist/plain/format/formatDate.js +1 -1
- package/dist/plain/format/formatDateRange.d.ts +3 -2
- package/dist/plain/format/formatDateRange.js +1 -1
- package/dist/plain/format/formatDateTime.d.ts +3 -2
- package/dist/plain/format/formatDateTime.js +1 -1
- package/dist/plain/format/formatDateTimeRange.d.ts +3 -2
- package/dist/plain/format/formatDateTimeRange.js +1 -1
- package/dist/plain/format/formatDateTimeToParts.d.ts +3 -2
- package/dist/plain/format/formatDateTimeToParts.js +1 -1
- package/dist/plain/format/formatDateToParts.d.ts +3 -2
- package/dist/plain/format/formatDateToParts.js +1 -1
- package/dist/plain/format/formatRelativeDate.d.ts +3 -14
- package/dist/plain/format/formatRelativeDateTime.d.ts +3 -14
- package/dist/plain/format/formatRelativeTime.d.ts +3 -14
- package/dist/plain/format/formatTime.d.ts +2 -1
- package/dist/plain/get/getNowUnit.d.ts +2 -3
- package/dist/plain/interval/splitIntervalByUnitDate.js +1 -1
- package/dist/regex/calendar-date.d.ts +19 -0
- package/dist/regex/calendar-date.js +19 -10
- package/dist/regex/calendar-zoned-date-time.d.ts +45 -0
- package/dist/regex/calendar-zoned-date-time.js +45 -40
- package/dist/regex/date-time.d.ts +18 -0
- package/dist/regex/date-time.js +18 -1
- package/dist/regex/date.d.ts +16 -0
- package/dist/regex/date.js +16 -1
- package/dist/regex/http-date.d.ts +15 -0
- package/dist/regex/http-date.js +15 -6
- package/dist/regex/leap-second.d.ts +12 -0
- package/dist/regex/leap-second.js +12 -1
- package/dist/regex/rfc-2822.d.ts +18 -0
- package/dist/regex/rfc-2822.js +18 -8
- package/dist/regex/rfc-3339.d.ts +19 -0
- package/dist/regex/rfc-3339.js +19 -6
- package/dist/regex/sql-date-time.d.ts +14 -0
- package/dist/regex/sql-date-time.js +14 -6
- package/dist/regex/time-zone-like.d.ts +21 -0
- package/dist/regex/time-zone-like.js +21 -0
- package/dist/regex/time.d.ts +59 -0
- package/dist/regex/time.js +59 -3
- package/dist/regex/unix.d.ts +21 -0
- package/dist/regex/unix.js +21 -0
- package/dist/regex/utc-date-time.d.ts +20 -0
- package/dist/regex/utc-date-time.js +20 -1
- package/dist/types/calendar-options.d.ts +11 -0
- package/dist/types/calendar-options.js +0 -0
- package/dist/types/datetime-format-options.d.ts +6 -0
- package/dist/types/datetime-format-options.js +0 -0
- package/dist/types/duration-unit.d.ts +5 -0
- package/dist/types/duration-unit.js +0 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.js +11 -0
- package/dist/types/now-unit.d.ts +5 -0
- package/dist/types/now-unit.js +0 -0
- package/dist/types/relative-date-time-unit.d.ts +6 -0
- package/dist/types/relative-date-time-unit.js +0 -0
- package/dist/types/relative-date-unit.d.ts +5 -0
- package/dist/types/relative-date-unit.js +0 -0
- package/dist/types/relative-time-options.d.ts +18 -0
- package/dist/types/relative-time-options.js +0 -0
- package/dist/types/relative-time-unit.d.ts +5 -0
- package/dist/types/relative-time-unit.js +0 -0
- package/dist/types/relative-unit.d.ts +6 -0
- package/dist/types/relative-unit.js +0 -0
- package/dist/types/unix-now-unit.d.ts +5 -0
- package/dist/types/unix-now-unit.js +0 -0
- package/dist/types/utc-now-unit.d.ts +5 -0
- package/dist/types/utc-now-unit.js +0 -0
- package/dist/unix/calculate/startOrEndOfUnix.d.ts +18 -0
- package/dist/unix/calculate/startOrEndOfUnix.js +18 -0
- package/dist/unix/format/formatCalendarUnix.d.ts +27 -1
- package/dist/unix/format/formatRelativeUnix.d.ts +1 -8
- package/dist/unix/format/formatUnix.d.ts +19 -0
- package/dist/unix/format/formatUnix.js +19 -0
- package/dist/unix/get/getUnixNowUnit.d.ts +2 -3
- package/dist/unix/parse/parseUnitFromUnix.d.ts +24 -0
- package/dist/unix/validate/isValidUnixUnit.d.ts +15 -0
- package/dist/utc/calculate/startOrEndOfUtc.d.ts +17 -0
- package/dist/utc/calculate/startOrEndOfUtc.js +17 -0
- package/dist/utc/format/formatCalendarUtc.d.ts +2 -2
- package/dist/utc/format/formatRelativeUtc.d.ts +2 -12
- package/dist/utc/format/formatUtc.d.ts +33 -0
- package/dist/utc/format/formatUtc.js +17 -0
- package/dist/utc/get/getUtcNowUnit.d.ts +2 -3
- package/dist/utc/parse/parseUnitFromUtc.d.ts +23 -0
- package/dist/zoned/format/formatRelativeZoned.d.ts +1 -8
- package/dist/zoned/format/formatTimeZoneName.d.ts +15 -0
- package/dist/zoned/format/formatZonedDateTime.d.ts +2 -1
- package/dist/zoned/format/formatZonedRange.d.ts +2 -1
- package/dist/zoned/format/formatZonedToParts.d.ts +2 -1
- package/dist/zoned/get/getDstTransitions.d.ts +18 -2
- package/dist/zoned/get/getZonedNowUnit.d.ts +24 -0
- package/dist/zoned/interval/intervalDifferenceZoned.d.ts +3 -3
- package/dist/zoned/interval/intervalDifferenceZoned.js +3 -3
- package/dist/zoned/interval/intervalXorZoned.d.ts +6 -4
- package/dist/zoned/interval/intervalXorZoned.js +6 -4
- package/dist/zoned/parse/getZonedOffsetAs.d.ts +11 -0
- package/dist/zoned/parse/parseUnitFromZoned.d.ts +24 -0
- package/package.json +25 -7
- package/skills/gmt-arithmetic/SKILL.md +83 -0
- package/skills/gmt-basics/SKILL.md +80 -0
- package/skills/gmt-integration/SKILL.md +73 -0
- package/skills/gmt-timezone/SKILL.md +80 -0
- package/skills/_artifacts/domain_map.yaml +0 -547
- package/skills/_artifacts/skill_spec.md +0 -67
- package/skills/_artifacts/skill_tree.yaml +0 -278
- package/skills/api-expansion-workflow/SKILL.md +0 -52
- package/skills/app-integration/SKILL.md +0 -38
- package/skills/calculate-dates/SKILL.md +0 -432
- package/skills/compare-dates/SKILL.md +0 -412
- package/skills/convert-types/SKILL.md +0 -305
- package/skills/durations/SKILL.md +0 -418
- package/skills/format-date-time/SKILL.md +0 -486
- package/skills/format-date-time/references/format-api.md +0 -34
- package/skills/format-relative-time/SKILL.md +0 -223
- package/skills/get-current/SKILL.md +0 -229
- package/skills/interval-ops/SKILL.md +0 -432
- package/skills/issue-creation/SKILL.md +0 -118
- package/skills/lint-package-suggestion/SKILL.md +0 -37
- package/skills/new-method-implementation/SKILL.md +0 -71
- package/skills/parse-date-time/SKILL.md +0 -349
- package/skills/pr-contribution/SKILL.md +0 -180
- package/skills/unit-test-generation/SKILL.md +0 -56
- package/skills/validate-dates/SKILL.md +0 -438
- package/skills/zoned-date-ops/SKILL.md +0 -422
- package/skills/zoned-date-ops/references/timezones.md +0 -13
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: durations
|
|
3
|
-
description: >
|
|
4
|
-
Parse, validate, combine, rebalance, and format ISO 8601 duration strings
|
|
5
|
-
(e.g. "P1DT2H30M"). Use isValidDuration to check a duration string is
|
|
6
|
-
well-formed, parseDuration to normalize one and optionally control its output
|
|
7
|
-
precision/rounding via smallestUnit, fractionalSecondDigits, and roundingMode,
|
|
8
|
-
addDuration / subtractDuration to combine two duration strings,
|
|
9
|
-
normalizeDuration to roll small units into larger ones (e.g. 90 minutes into 1
|
|
10
|
-
hour 30 minutes) via largestUnit/smallestUnit/
|
|
11
|
-
roundingIncrement/roundingMode/relativeTo, getDurationUnit to read one stored
|
|
12
|
-
component, durationAs to total a duration into a single unit, negateDuration /
|
|
13
|
-
absDuration to flip or drop its sign, getDurationSign to test whether it is
|
|
14
|
-
negative/zero/positive, compareDurations to order two durations by length, and
|
|
15
|
-
formatDuration to render a duration as a human-readable, locale-aware string
|
|
16
|
-
(e.g. "1 day, 2 hours, and 30 minutes").
|
|
17
|
-
sources:
|
|
18
|
-
- 'northguild/gmt:packages/gmt/src/duration/index.ts'
|
|
19
|
-
- 'northguild/gmt:packages/gmt/src/duration/calculate/getDurationUnit.ts'
|
|
20
|
-
- 'northguild/gmt:packages/gmt/src/duration/calculate/durationAs.ts'
|
|
21
|
-
- 'northguild/gmt:packages/gmt/src/duration/calculate/negateDuration.ts'
|
|
22
|
-
- 'northguild/gmt:packages/gmt/src/duration/calculate/absDuration.ts'
|
|
23
|
-
- 'northguild/gmt:packages/gmt/src/duration/calculate/getDurationSign.ts'
|
|
24
|
-
- 'northguild/gmt:packages/gmt/src/duration/compare/compareDurations.ts'
|
|
25
|
-
- 'northguild/gmt:packages/gmt/src/plain/calculate/diffDateAsDuration.ts'
|
|
26
|
-
- 'northguild/gmt:packages/gmt/src/plain/calculate/diffDateTimeAsDuration.ts'
|
|
27
|
-
- 'northguild/gmt:packages/gmt/src/zoned/calculate/diffZonedAsDuration.ts'
|
|
28
|
-
- 'northguild/gmt:packages/gmt/src/unix/calculate/diffUnixAsDuration.ts'
|
|
29
|
-
- 'northguild/gmt:packages/gmt/src/utc/calculate/diffUtcAsDuration.ts'
|
|
30
|
-
metadata:
|
|
31
|
-
type: core
|
|
32
|
-
library: '@northguild/gmt'
|
|
33
|
-
library_version: '1.14.2'
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
# Durations
|
|
37
|
-
|
|
38
|
-
Use this skill when you need to parse, validate, re-normalize, or format ISO 8601 duration strings — distinct from date/time arithmetic, which takes a `{ unit: number }` object rather than a duration string. See the `calculate-dates` skill for `add*`/`subtract*`/`diff*`.
|
|
39
|
-
|
|
40
|
-
## Setup
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
import {
|
|
44
|
-
absDuration,
|
|
45
|
-
addDuration,
|
|
46
|
-
compareDurations,
|
|
47
|
-
durationAs,
|
|
48
|
-
formatDuration,
|
|
49
|
-
getDurationSign,
|
|
50
|
-
getDurationUnit,
|
|
51
|
-
isValidDuration,
|
|
52
|
-
negateDuration,
|
|
53
|
-
normalizeDuration,
|
|
54
|
-
parseDuration,
|
|
55
|
-
subtractDuration,
|
|
56
|
-
} from "@northguild/gmt";
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Core Patterns
|
|
60
|
-
|
|
61
|
-
### Validate an ISO 8601 duration string
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
const valid = isValidDuration("P1DT2H30M"); // true
|
|
65
|
-
const invalid = isValidDuration("not a duration"); // false
|
|
66
|
-
const empty = isValidDuration(""); // false
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Parse and re-normalize a duration string
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
const result = parseDuration("P1DT2H30M"); // "P1DT2H30M"
|
|
73
|
-
const invalid = parseDuration("not a duration"); // ""
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Round a duration's output precision
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
// smallestUnit rounds down to whole units of the given granularity
|
|
80
|
-
const rounded = parseDuration("PT1.5S", { smallestUnit: "second" }); // "PT1S"
|
|
81
|
-
|
|
82
|
-
// fractionalSecondDigits pads/truncates the fractional-seconds component
|
|
83
|
-
const padded = parseDuration("PT1.5S", { fractionalSecondDigits: 3 }); // "PT1.500S"
|
|
84
|
-
|
|
85
|
-
// roundingMode controls how rounding is performed (default halfExpand)
|
|
86
|
-
const truncated = parseDuration("PT1.9S", {
|
|
87
|
-
smallestUnit: "second",
|
|
88
|
-
roundingMode: "trunc",
|
|
89
|
-
}); // "PT1S"
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Combine two duration strings
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
const combined = addDuration("P1D", "PT2H"); // "P1DT2H"
|
|
96
|
-
const remainder = subtractDuration("P1D", "PT2H"); // "PT22H"
|
|
97
|
-
const negative = subtractDuration("PT1H", "PT2H"); // "-PT1H"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Roll small units into larger ones
|
|
101
|
-
|
|
102
|
-
```ts
|
|
103
|
-
// largestUnit promotes into a bigger unit
|
|
104
|
-
const promoted = normalizeDuration("PT90M", { largestUnit: "hour" }); // "PT1H30M"
|
|
105
|
-
|
|
106
|
-
// smallestUnit rounds off sub-unit precision, no largestUnit required
|
|
107
|
-
const rounded = normalizeDuration("PT90M30S", { smallestUnit: "minute" }); // "PT91M"
|
|
108
|
-
|
|
109
|
-
// no options defaults to largestUnit: "auto" — reformats without promoting
|
|
110
|
-
const reformatted = normalizeDuration("PT90M"); // "PT90M" (unchanged)
|
|
111
|
-
|
|
112
|
-
// calendar units (year/month/week) need relativeTo as a reference point
|
|
113
|
-
const noRef = normalizeDuration("P45D", { largestUnit: "month" }); // ""
|
|
114
|
-
const withRef = normalizeDuration("P45D", {
|
|
115
|
-
largestUnit: "month",
|
|
116
|
-
relativeTo: "2024-01-01",
|
|
117
|
-
}); // "P1M14D"
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Read one component out of a duration
|
|
121
|
-
|
|
122
|
-
`getDurationUnit` returns the component **as stored**, not a converted total.
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
const hours = getDurationUnit("P1DT2H30M", "hours"); // 2
|
|
126
|
-
const minutes = getDurationUnit("P1DT2H30M", "minutes"); // 30
|
|
127
|
-
|
|
128
|
-
// "PT90M" stores 90 in its minutes field and nothing in its hours field
|
|
129
|
-
const noHours = getDurationUnit("PT90M", "hours"); // 0
|
|
130
|
-
|
|
131
|
-
// a negative duration stores every field as negative
|
|
132
|
-
const negative = getDurationUnit("-P1DT2H", "hours"); // -2
|
|
133
|
-
|
|
134
|
-
// reading a field is not a unit conversion, so calendar units need no relativeTo
|
|
135
|
-
const months = getDurationUnit("P1M", "months"); // 1
|
|
136
|
-
|
|
137
|
-
const invalid = getDurationUnit("not a duration", "hours"); // null
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Total a duration into a single unit
|
|
141
|
-
|
|
142
|
-
`durationAs` converts the whole duration and returns a fractional number.
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
const asHours = durationAs("P1DT2H30M", "hours"); // 26.5
|
|
146
|
-
const asMinutes = durationAs("P1DT2H30M", "minutes"); // 1590
|
|
147
|
-
const asDays = durationAs("PT36H", "days"); // 1.5 — fractional, not rounded
|
|
148
|
-
|
|
149
|
-
// calendar units need a relativeTo anchor, in either direction
|
|
150
|
-
const noAnchor = durationAs("P1M", "days"); // null
|
|
151
|
-
const feb = durationAs("P1M", "days", { relativeTo: "2024-02-01" }); // 29
|
|
152
|
-
const jan = durationAs("P1M", "days", { relativeTo: "2024-01-01" }); // 31
|
|
153
|
-
|
|
154
|
-
// a zoned anchor resolves real elapsed time across a DST transition
|
|
155
|
-
const springForward = durationAs("P1D", "hours", {
|
|
156
|
-
relativeTo: "2024-03-10T00:00:00-05:00[America/New_York]",
|
|
157
|
-
}); // 23
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Flip, drop, or test a duration's sign
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
const negated = negateDuration("P1DT2H"); // "-P1DT2H"
|
|
164
|
-
const backAgain = negateDuration("-P1DT2H"); // "P1DT2H"
|
|
165
|
-
const positive = absDuration("-P1DT2H"); // "P1DT2H"
|
|
166
|
-
|
|
167
|
-
const sign = getDurationSign("-P1DT2H"); // -1
|
|
168
|
-
const zero = getDurationSign("PT0S"); // 0
|
|
169
|
-
const invalid = getDurationSign("not a duration"); // null
|
|
170
|
-
|
|
171
|
-
// all three are pure sign operations — calendar units work, no relativeTo needed
|
|
172
|
-
const negatedMonth = negateDuration("P1Y2M"); // "-P1Y2M"
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### Compare two durations by length
|
|
176
|
-
|
|
177
|
-
```ts
|
|
178
|
-
const longer = compareDurations("PT1H", "PT30M"); // 1
|
|
179
|
-
const shorter = compareDurations("PT30M", "PT1H"); // -1
|
|
180
|
-
|
|
181
|
-
// equal by length, not by spelling
|
|
182
|
-
const equal = compareDurations("PT60M", "PT1H"); // 0
|
|
183
|
-
|
|
184
|
-
// unlike addDuration, Temporal.Duration.compare DOES take relativeTo, so
|
|
185
|
-
// calendar-unit durations are comparable — the anchor decides the answer
|
|
186
|
-
const noAnchor = compareDurations("P1M", "P30D"); // null
|
|
187
|
-
const fromJan = compareDurations("P1M", "P30D", { relativeTo: "2024-01-01" }); // 1
|
|
188
|
-
const fromFeb = compareDurations("P1M", "P30D", { relativeTo: "2024-02-01" }); // -1
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### Render a duration as human-readable text
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
const long = formatDuration("P1DT2H30M", "en-US");
|
|
195
|
-
// "1 day, 2 hours, and 30 minutes"
|
|
196
|
-
|
|
197
|
-
const short = formatDuration("P1DT2H30M", "en-US", { style: "short" });
|
|
198
|
-
// "1 day, 2 hr, & 30 min"
|
|
199
|
-
|
|
200
|
-
const narrow = formatDuration("P1DT2H30M", "en-US", { style: "narrow" });
|
|
201
|
-
// "1d, 2h, & 30m"
|
|
202
|
-
|
|
203
|
-
// zero-valued components are omitted by default
|
|
204
|
-
const noZeros = formatDuration("P1DT0H30M", "en-US"); // "1 day and 30 minutes"
|
|
205
|
-
const withZeros = formatDuration("P1DT0H30M", "en-US", { zero: true });
|
|
206
|
-
// "0 years, 0 months, 0 weeks, 1 day, 0 hours, 30 minutes, and 0 seconds"
|
|
207
|
-
|
|
208
|
-
// a zero-length duration always renders "0 seconds"
|
|
209
|
-
const zero = formatDuration("PT0S", "en-US"); // "0 seconds"
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### Bridge a diff into an ISO duration string
|
|
213
|
-
|
|
214
|
-
`diffDate`/`diffDateTime`/`diffZoned`/`diffUnix`/`diffUtc` return a single-unit number (or a `Record<Unit, number>` for an array of units). Their `*AsDuration` siblings return an ISO 8601 duration string instead — the same string shape every other function in this skill consumes/produces.
|
|
215
|
-
|
|
216
|
-
```ts
|
|
217
|
-
import { diffDateAsDuration, diffZonedAsDuration } from "@northguild/gmt";
|
|
218
|
-
|
|
219
|
-
const span = diffDateAsDuration("2024-03-10", "2024-04-05", "days"); // "P26D"
|
|
220
|
-
|
|
221
|
-
const negative = diffDateAsDuration("2024-04-05", "2024-03-10", "days"); // "-P26D"
|
|
222
|
-
|
|
223
|
-
const zoned = diffZonedAsDuration(
|
|
224
|
-
"2024-03-09T12:00:00-05:00[America/New_York]",
|
|
225
|
-
"2024-03-11T12:00:00-04:00[America/New_York]",
|
|
226
|
-
"days",
|
|
227
|
-
); // "P1DT23H" — real elapsed time across a DST spring-forward transition
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
Feed the result straight into a `duration` namespace function, e.g. `formatDuration(diffDateAsDuration(a, b, "days"), "en-US")`.
|
|
231
|
-
|
|
232
|
-
## Common Mistakes
|
|
233
|
-
|
|
234
|
-
### HIGH Confusing duration strings with unit objects
|
|
235
|
-
|
|
236
|
-
Wrong:
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
import { addDate } from "@northguild/gmt";
|
|
240
|
-
|
|
241
|
-
// addDate takes a { unit: number } object, not an ISO duration string
|
|
242
|
-
const result = addDate("2024-03-15", "P5D");
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Correct:
|
|
246
|
-
|
|
247
|
-
```ts
|
|
248
|
-
import { addDate } from "@northguild/gmt";
|
|
249
|
-
|
|
250
|
-
const result = addDate("2024-03-15", { days: 5 }); // "2024-03-20"
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Source: packages/gmt/src/plain/calculate/addDate.ts — accepts `Partial<Record<DateDurationUnit, number>>`, not a duration string
|
|
254
|
-
|
|
255
|
-
### MEDIUM Not validating before parsing
|
|
256
|
-
|
|
257
|
-
Wrong:
|
|
258
|
-
|
|
259
|
-
```ts
|
|
260
|
-
const result = parseDuration(userInput);
|
|
261
|
-
// assume result is a valid duration string
|
|
262
|
-
useDuration(result);
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Correct:
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
import { isValidDuration, parseDuration } from "@northguild/gmt";
|
|
269
|
-
|
|
270
|
-
if (!isValidDuration(userInput)) {
|
|
271
|
-
throw new Error("Invalid duration");
|
|
272
|
-
}
|
|
273
|
-
const result = parseDuration(userInput);
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
Source: packages/gmt/src/duration/parse/parseDuration.ts — returns `""` on invalid input rather than throwing
|
|
277
|
-
|
|
278
|
-
### MEDIUM Expecting a best-effort answer for calendar units without relativeTo
|
|
279
|
-
|
|
280
|
-
`"P1M"` has no fixed length, so nothing in this namespace will guess one for you. Every function that has to *measure* a duration returns its sentinel (`""` for strings, `null` for numbers) when a year/month/week is involved and no `relativeTo` anchor was supplied — it does not fall back to a nominal 30-day month.
|
|
281
|
-
|
|
282
|
-
Wrong:
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
import { addDuration, compareDurations, durationAs } from "@northguild/gmt";
|
|
286
|
-
|
|
287
|
-
const combined = addDuration("P1Y", "P1M"); // ""
|
|
288
|
-
const asDays = durationAs("P1M", "days"); // null — not 30
|
|
289
|
-
const ordered = compareDurations("P1M", "P30D"); // null
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
Correct: supply `relativeTo` wherever the function accepts one. Note the three tiers, which differ by what Temporal itself allows:
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
import {
|
|
296
|
-
absDuration,
|
|
297
|
-
compareDurations,
|
|
298
|
-
durationAs,
|
|
299
|
-
getDurationSign,
|
|
300
|
-
getDurationUnit,
|
|
301
|
-
negateDuration,
|
|
302
|
-
normalizeDuration,
|
|
303
|
-
} from "@northguild/gmt";
|
|
304
|
-
|
|
305
|
-
// 1. Takes relativeTo — pass it and calendar units work
|
|
306
|
-
durationAs("P1M", "days", { relativeTo: "2024-02-01" }); // 29
|
|
307
|
-
compareDurations("P1M", "P30D", { relativeTo: "2024-01-01" }); // 1
|
|
308
|
-
normalizeDuration("P45D", { largestUnit: "month", relativeTo: "2024-01-01" }); // "P1M14D"
|
|
309
|
-
|
|
310
|
-
// 2. Never needs relativeTo — reads a stored field or flips a sign, no measuring
|
|
311
|
-
getDurationUnit("P1M", "months"); // 1
|
|
312
|
-
getDurationSign("-P1Y"); // -1
|
|
313
|
-
negateDuration("P1Y2M"); // "-P1Y2M"
|
|
314
|
-
absDuration("-P1Y2M"); // "P1Y2M"
|
|
315
|
-
|
|
316
|
-
// 3. Has no relativeTo option at all — use addDate/subtractDate with a unit object
|
|
317
|
-
addDuration("P1Y", "P1M"); // "" — no way to make this work
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
`durationAs`'s rule cuts in both directions, and the requested-unit half bites even on day/time-only input:
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
durationAs("PT36H", "weeks"); // null — a week is a calendar quantity to Temporal
|
|
324
|
-
durationAs("P1M", "hours"); // null — the input's month component needs an anchor
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
The anchor decides the answer rather than merely unblocking it: `compareDurations("P1M", "P30D", { relativeTo: "2024-01-01" })` is `1` (January is 31 days) while the same call anchored to `"2024-02-01"` is `-1` (February 2024 is 29). It matters for non-calendar units too when the anchor names a zoned instant — `durationAs("P1D", "hours", { relativeTo: "2024-03-10T00:00:00-05:00[America/New_York]" })` is `23`, not `24`, because that day spans a DST spring-forward.
|
|
328
|
-
|
|
329
|
-
Source: packages/gmt/src/duration/calculate/addDuration.ts (`Temporal.Duration.prototype.add`/`.subtract` have no `relativeTo` option, so calendar-unit operands throw and result in `""`), packages/gmt/src/duration/calculate/durationAs.ts and packages/gmt/src/duration/compare/compareDurations.ts (`.total()` and `Temporal.Duration.compare` both accept `relativeTo` and require it for calendar units)
|
|
330
|
-
|
|
331
|
-
`relativeTo` also accepts a GMT calendar-annotated `PlainDate` string (E5, e.g. `"5784-06-15[u-ca=hebrew]"`, as produced by `convertDateToCalendar`) — not Temporal's own differently-shaped `[u-ca=...]` convention. `durationAs`, `compareDurations`, and `normalizeDuration` all resolve it the same way:
|
|
332
|
-
|
|
333
|
-
```ts
|
|
334
|
-
durationAs("P1Y", "days", { relativeTo: "5784-06-15[u-ca=hebrew]" }); // 385, not the 366 a Gregorian P1Y would total — a Hebrew leap year
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### HIGH Reading a component with getDurationUnit when you wanted a total
|
|
338
|
-
|
|
339
|
-
`getDurationUnit` reads the named field **as stored**; `durationAs` converts the whole duration. They disagree whenever the duration isn't already spelled in the unit you're asking for, and `getDurationUnit`'s wrong answer is a plausible-looking `0` rather than an error.
|
|
340
|
-
|
|
341
|
-
Wrong:
|
|
342
|
-
|
|
343
|
-
```ts
|
|
344
|
-
import { getDurationUnit } from "@northguild/gmt";
|
|
345
|
-
|
|
346
|
-
// "PT90M" holds 90 in its minutes field and nothing in its hours field
|
|
347
|
-
const hours = getDurationUnit("PT90M", "hours"); // 0, not 1.5
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Correct:
|
|
351
|
-
|
|
352
|
-
```ts
|
|
353
|
-
import { durationAs, getDurationUnit } from "@northguild/gmt";
|
|
354
|
-
|
|
355
|
-
const total = durationAs("PT90M", "hours"); // 1.5 — the whole duration in hours
|
|
356
|
-
const stored = getDurationUnit("P1DT2H30M", "hours"); // 2 — the hours component itself
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
`durationAs` returns a fractional total and never rounds — `durationAs("P1DT2H30M", "days")` is `1.1041666666666667`. If you want a rebalanced duration *string* rather than a number, reach for `normalizeDuration("PT90M", { largestUnit: "hour" })` instead.
|
|
360
|
-
|
|
361
|
-
Source: packages/gmt/src/duration/calculate/getDurationUnit.ts (reads `Temporal.Duration`'s field directly) vs. packages/gmt/src/duration/calculate/durationAs.ts (wraps `Temporal.Duration.prototype.total`)
|
|
362
|
-
|
|
363
|
-
### MEDIUM Assuming `normalizeDuration`'s default is always `relativeTo`-free
|
|
364
|
-
|
|
365
|
-
Wrong:
|
|
366
|
-
|
|
367
|
-
```ts
|
|
368
|
-
import { normalizeDuration } from "@northguild/gmt";
|
|
369
|
-
|
|
370
|
-
// input already has a month component — the "auto" default does NOT
|
|
371
|
-
// exempt calendar-unit inputs, this still throws internally and returns ""
|
|
372
|
-
const result = normalizeDuration("P1M"); // ""
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Correct: pass `relativeTo` whenever a calendar unit (year/month/week) is involved — either as an explicit `largestUnit`, or because the input duration itself already has a nonzero year/month/week component, even under the default `{ largestUnit: "auto" }`.
|
|
376
|
-
|
|
377
|
-
```ts
|
|
378
|
-
import { normalizeDuration } from "@northguild/gmt";
|
|
379
|
-
|
|
380
|
-
const result = normalizeDuration("P1M", {
|
|
381
|
-
largestUnit: "day",
|
|
382
|
-
relativeTo: "2024-01-01",
|
|
383
|
-
}); // "P31D"
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Source: packages/gmt/src/duration/normalize/normalizeDuration.ts — `Temporal.Duration.prototype.round` requires `relativeTo` for any week-or-larger unit, whether requested via `largestUnit` or already present in the duration being rounded
|
|
387
|
-
|
|
388
|
-
### MEDIUM Expecting `formatDuration` to match native `Intl.DurationFormat` output exactly
|
|
389
|
-
|
|
390
|
-
`formatDuration` does not use `Intl.DurationFormat` — that constructor is absent entirely on Node 20/22 (only ships natively on Node 24+), and GMT does not carry a polyfill dependency for it. Instead, `formatDuration` builds its output from `Intl.NumberFormat({ style: "unit" })` per component plus `Intl.ListFormat` for joining — both universally available, so behavior is identical across all supported Node versions, but output is not guaranteed byte-for-byte identical to what native `Intl.DurationFormat` would produce (e.g. no `"digital"` `01:30:00`-style output is supported).
|
|
391
|
-
|
|
392
|
-
Source: packages/gmt/src/duration/format/formatDuration.ts
|
|
393
|
-
|
|
394
|
-
### MEDIUM Passing an array of units to a `*AsDuration` bridge function
|
|
395
|
-
|
|
396
|
-
Wrong:
|
|
397
|
-
|
|
398
|
-
```ts
|
|
399
|
-
import { diffDateAsDuration } from "@northguild/gmt";
|
|
400
|
-
|
|
401
|
-
// diffDate accepts unit | unit[], but diffDateAsDuration only accepts a
|
|
402
|
-
// single unit — an array here fails validation and returns ""
|
|
403
|
-
const result = diffDateAsDuration("2024-03-10", "2024-04-05", ["days"]); // ""
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
Correct: pass a single unit. It only sets the resulting duration's `largestUnit` — an ISO duration string already expresses a full multi-unit breakdown (e.g. `"P1DT23H"`) without needing an array of requested units the way `diffDate`'s `Record<Unit, number>` return shape does.
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
import { diffDateAsDuration } from "@northguild/gmt";
|
|
410
|
-
|
|
411
|
-
const result = diffDateAsDuration("2024-03-10", "2024-04-05", "days"); // "P26D"
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
Source: packages/gmt/src/plain/calculate/diffDateAsDuration.ts (and its `diffDateTimeAsDuration`/`diffZonedAsDuration`/`diffUnixAsDuration`/`diffUtcAsDuration` siblings) — signature takes `unit: DateDurationUnit`, not `unit: DateDurationUnit | DateDurationUnit[]` like their counterparts
|
|
415
|
-
|
|
416
|
-
## References
|
|
417
|
-
|
|
418
|
-
- [Temporal.Duration](https://tc39.es/proposal-temporal/docs/duration.html)
|