@ultimat3/time
Advanced tools
+9
-0
@@ -159,2 +159,11 @@ # @ultimat3/time — agent notes | ||
| breaks the signed-duration contract `toSeconds` is built on. | ||
| - **`toMs(duration, subject?, option?)` names the caller's knob in the refusal, `As of 2026-08-26`.** | ||
| `pass a finite duration to toMs` names a function an app author reached THROUGH rather than | ||
| wrote: `@ultimat3/testing`'s `clock.advance('3s')` hands its argument straight down, and so does | ||
| any wrapper an app builds. The two names are the same two, in the same order, that | ||
| `@ultimat3/jobs`' `finiteDurationMs` takes — one shape, not two. They are **optional** here and | ||
| required there because this one is published API: making them required is `TS2554` at every | ||
| existing call site in every app, a major for a better sentence. The defaults reproduce today's | ||
| message byte for byte, so the only observable change is at a call site that supplies them. | ||
| `toSeconds` threads them too and defaults its subject to **`toSeconds`**, not to the delegate. | ||
| - Tests must cover a spring-forward gap, a fall-back overlap and a non-hour offset zone. | ||
@@ -161,0 +170,0 @@ |
+3
-3
| { | ||
| "name": "@ultimat3/time", | ||
| "version": "17.0.0", | ||
| "version": "18.0.0", | ||
| "description": "UTC instants, DST-correct zone math, cron, durations and Intl formatting with an explicit timezone", | ||
@@ -30,3 +30,3 @@ "license": "MIT", | ||
| "engines": { | ||
| "bun": ">=1.3.0" | ||
| "bun": ">=1.4.0" | ||
| }, | ||
@@ -38,4 +38,4 @@ "scripts": { | ||
| "dependencies": { | ||
| "@ultimat3/core": "17.0.0" | ||
| "@ultimat3/core": "18.0.0" | ||
| } | ||
| } |
+24
-4
@@ -78,6 +78,18 @@ /** | ||
| * `@ultimat3/notify`'s `toDurationMs` does; narrowing here would break both. | ||
| * | ||
| * `subject` and `option` name the knob the APP AUTHOR wrote, for the refusal only. They default to | ||
| * this function's own name, which is the right answer for the caller who typed `toMs(…)` and the | ||
| * wrong one for every caller reached THROUGH it: `clock.advance('3s')` in `@ultimat3/testing` | ||
| * passes its argument straight down, and `pass a finite duration to toMs` sends that author | ||
| * looking for a knob their code does not contain. Same two names, same order, as | ||
| * `@ultimat3/jobs`' `finiteDurationMs` — one shape, not two. | ||
| * | ||
| * OPTIONAL rather than required, unlike that one, because this function is published API: a | ||
| * required parameter is `TS2554` at every existing call site in every app, which is a major for a | ||
| * better sentence. The default reproduces today's message byte for byte, so supplying them is the | ||
| * only observable change. | ||
| */ | ||
| export function toMs(duration: string | number): number { | ||
| export function toMs(duration: string | number, subject = 'toMs', option = 'duration'): number { | ||
| return typeof duration === 'number' | ||
| ? finiteOption('toMs', 'duration', duration) | ||
| ? finiteOption(subject, option, duration) | ||
| : parseDuration(duration); | ||
@@ -91,5 +103,13 @@ } | ||
| * framework's one statement of this, and `signed()` there is why zero never comes back as `-0`. | ||
| * | ||
| * The screen is `toMs`'s, threaded — but the default subject is `toSeconds`, because that is the | ||
| * name a direct caller wrote. Naming the delegate would point them at a function they never typed, | ||
| * which is the whole defect this pair of parameters exists to fix. | ||
| */ | ||
| export function toSeconds(duration: string | number): number { | ||
| const ms = toMs(duration); | ||
| export function toSeconds( | ||
| duration: string | number, | ||
| subject = 'toSeconds', | ||
| option = 'duration', | ||
| ): number { | ||
| const ms = toMs(duration, subject, option); | ||
| const seconds = Math.round(Math.abs(ms) / SECOND); | ||
@@ -96,0 +116,0 @@ if (seconds === 0) return 0; |
120204
1.77%2228
0.91%+ Added
+ Added
- Removed
Updated