@ultimat3/time 17.0.0 → 18.0.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/CLAUDE.md +9 -0
- package/package.json +3 -3
- package/src/duration.ts +24 -4
package/CLAUDE.md
CHANGED
|
@@ -156,6 +156,15 @@
|
|
|
156
156
|
`parseDuration` accepts a leading `-`, and `toSeconds(-3000)` is a tested `-3` — and legitimately
|
|
157
157
|
fractional. A caller that needs whole non-negative milliseconds narrows on top; narrowing here
|
|
158
158
|
breaks the signed-duration contract `toSeconds` is built on.
|
|
159
|
+
- **`toMs(duration, subject?, option?)` names the caller's knob in the refusal, `As of 2026-08-26`.**
|
|
160
|
+
`pass a finite duration to toMs` names a function an app author reached THROUGH rather than
|
|
161
|
+
wrote: `@ultimat3/testing`'s `clock.advance('3s')` hands its argument straight down, and so does
|
|
162
|
+
any wrapper an app builds. The two names are the same two, in the same order, that
|
|
163
|
+
`@ultimat3/jobs`' `finiteDurationMs` takes — one shape, not two. They are **optional** here and
|
|
164
|
+
required there because this one is published API: making them required is `TS2554` at every
|
|
165
|
+
existing call site in every app, a major for a better sentence. The defaults reproduce today's
|
|
166
|
+
message byte for byte, so the only observable change is at a call site that supplies them.
|
|
167
|
+
`toSeconds` threads them too and defaults its subject to **`toSeconds`**, not to the delegate.
|
|
159
168
|
- Tests must cover a spring-forward gap, a fall-back overlap and a non-hour offset zone.
|
|
160
169
|
|
|
161
170
|
## Commands
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/time",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
4
|
"description": "UTC instants, DST-correct zone math, cron, durations and Intl formatting with an explicit timezone",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
"LICENSE"
|
|
28
28
|
],
|
|
29
29
|
"engines": {
|
|
30
|
-
"bun": ">=1.
|
|
30
|
+
"bun": ">=1.4.0"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
34
34
|
"test": "bun test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@ultimat3/core": "
|
|
37
|
+
"@ultimat3/core": "18.0.0"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/duration.ts
CHANGED
|
@@ -75,10 +75,22 @@ export function parseDuration(input: string): number {
|
|
|
75
75
|
* accepts a leading `-` and `toSeconds(-3000)` is a tested `-3` — and legitimately fractional.
|
|
76
76
|
* A caller that needs whole non-negative milliseconds narrows on top of this, which is what
|
|
77
77
|
* `@ultimat3/notify`'s `toDurationMs` does; narrowing here would break both.
|
|
78
|
+
*
|
|
79
|
+
* `subject` and `option` name the knob the APP AUTHOR wrote, for the refusal only. They default to
|
|
80
|
+
* this function's own name, which is the right answer for the caller who typed `toMs(…)` and the
|
|
81
|
+
* wrong one for every caller reached THROUGH it: `clock.advance('3s')` in `@ultimat3/testing`
|
|
82
|
+
* passes its argument straight down, and `pass a finite duration to toMs` sends that author
|
|
83
|
+
* looking for a knob their code does not contain. Same two names, same order, as
|
|
84
|
+
* `@ultimat3/jobs`' `finiteDurationMs` — one shape, not two.
|
|
85
|
+
*
|
|
86
|
+
* OPTIONAL rather than required, unlike that one, because this function is published API: a
|
|
87
|
+
* required parameter is `TS2554` at every existing call site in every app, which is a major for a
|
|
88
|
+
* better sentence. The default reproduces today's message byte for byte, so supplying them is the
|
|
89
|
+
* only observable change.
|
|
78
90
|
*/
|
|
79
|
-
export function toMs(duration: string | number): number {
|
|
91
|
+
export function toMs(duration: string | number, subject = 'toMs', option = 'duration'): number {
|
|
80
92
|
return typeof duration === 'number'
|
|
81
|
-
? finiteOption(
|
|
93
|
+
? finiteOption(subject, option, duration)
|
|
82
94
|
: parseDuration(duration);
|
|
83
95
|
}
|
|
84
96
|
|
|
@@ -87,9 +99,17 @@ export function toMs(duration: string | number): number {
|
|
|
87
99
|
* and `'-1500ms'` was -1, so a signed duration and its mirror did not answer mirrored seconds.
|
|
88
100
|
* The sign is carried out and the MAGNITUDE rounded — `packages/money/src/rounding.ts` is the
|
|
89
101
|
* framework's one statement of this, and `signed()` there is why zero never comes back as `-0`.
|
|
102
|
+
*
|
|
103
|
+
* The screen is `toMs`'s, threaded — but the default subject is `toSeconds`, because that is the
|
|
104
|
+
* name a direct caller wrote. Naming the delegate would point them at a function they never typed,
|
|
105
|
+
* which is the whole defect this pair of parameters exists to fix.
|
|
90
106
|
*/
|
|
91
|
-
export function toSeconds(
|
|
92
|
-
|
|
107
|
+
export function toSeconds(
|
|
108
|
+
duration: string | number,
|
|
109
|
+
subject = 'toSeconds',
|
|
110
|
+
option = 'duration',
|
|
111
|
+
): number {
|
|
112
|
+
const ms = toMs(duration, subject, option);
|
|
93
113
|
const seconds = Math.round(Math.abs(ms) / SECOND);
|
|
94
114
|
if (seconds === 0) return 0;
|
|
95
115
|
return ms < 0 ? -seconds : seconds;
|