@cosyte/dates 0.1.1 → 0.1.3
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/CHANGELOG.md +18 -0
- package/LICENSE +1 -1
- package/README.md +38 -24
- package/dist/index.d.ts +117 -21
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/package.json +19 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8c85aa7: The README follows the house layout, with sections for support and security, and the installation page names Node 26, which CI now tests.
|
|
8
|
+
|
|
9
|
+
## 0.1.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 3b32ce3: Validation messages now write a range with "to", for example `month must be an integer in 1 to 12`, where they wrote `1-12`.
|
|
14
|
+
|
|
15
|
+
The issue codes (`out-of-range`, `year-not-four-digits`) and the error classes are unchanged, so code that branches on `code` or on the error class needs no change. Code that matches the message text has to follow: the year, month, day, hour, minute, second, and `offsetMinutes` range messages, and the `RangeError` an out-of-range `offsetMinutes` option throws, all use "to" now. The same wording reaches the type declarations' doc comments.
|
|
16
|
+
|
|
17
|
+
- fa6a741: The type declarations now carry a usage example for every exported function and error class, so an editor shows one on hover.
|
|
18
|
+
|
|
19
|
+
Runtime behavior is unchanged. The repository's build, lint, format, type-check and test setup moved to the shared Cosyte configs, which add a 90% coverage gate, and CI now runs on Node 22 and Node 24.
|
|
20
|
+
|
|
3
21
|
## 0.1.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<a href="https://cosyte.com">
|
|
2
2
|
<picture>
|
|
3
3
|
<source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
|
|
4
|
-
<img alt="Cosyte
|
|
4
|
+
<img alt="The Cosyte logo on its own white ground: the icon beside the word Cosyte." src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
|
|
5
5
|
</picture>
|
|
6
6
|
</a>
|
|
7
7
|
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
> Work with the dates the @cosyte/* parsers give you, without losing what the wire actually said.
|
|
11
11
|
|
|
12
12
|
[](https://www.npmjs.com/package/@cosyte/dates)
|
|
13
|
-
[](https://github.com/cosyte/dates/actions/workflows/ci.yml)
|
|
14
|
+
[](https://github.com/cosyte/dates/blob/main/LICENSE)
|
|
15
15
|
[](https://nodejs.org)
|
|
16
16
|
|
|
17
17
|
Date and time utilities for the @cosyte/* healthcare parsers: precision-preserving conversion and validation that never guesses a timezone.
|
|
@@ -24,29 +24,29 @@ value with no offset is the sender's local time by the standard's own words. The
|
|
|
24
24
|
parsers preserve exactly that and refuse to invent the rest, which is why they
|
|
25
25
|
carry no date library at all.
|
|
26
26
|
|
|
27
|
-
That fidelity leaves a gap: a developer still has to validate, compare and
|
|
27
|
+
That fidelity leaves a gap: a developer still has to validate, compare, and
|
|
28
28
|
convert those values, and the obvious tools all model an absolute instant. Reach
|
|
29
|
-
for `new Date('1988')` and you have silently acquired a month, a day, a midnight
|
|
29
|
+
for `new Date('1988')` and you have silently acquired a month, a day, a midnight,
|
|
30
30
|
and a timezone that nobody sent you. This package is the opt-in companion that
|
|
31
31
|
closes the gap without any of that, so the parsers stay zero-dependency and
|
|
32
32
|
nobody has their date library chosen for them.
|
|
33
33
|
|
|
34
34
|
## Status
|
|
35
35
|
|
|
36
|
-
**0.1.
|
|
37
|
-
functions, the shape of `DateParts`, the issue codes and the error classes are
|
|
36
|
+
**The 0.1 line. The public API is settled and safe to depend on.** The exported
|
|
37
|
+
functions, the shape of `DateParts`, the issue codes, and the error classes are
|
|
38
38
|
what this package intends to keep. While the version is below 1.0.0, a breaking
|
|
39
39
|
change to any of them ships in a new minor version, never a patch, and the
|
|
40
40
|
changelog says what broke and what to do instead.
|
|
41
41
|
|
|
42
|
-
Not covered
|
|
42
|
+
Not covered on the 0.1 line, and named so the boundary is not a surprise:
|
|
43
43
|
|
|
44
44
|
- **No parsing of vendor date text.** This package consumes parts that a
|
|
45
45
|
`@cosyte/*` parser already produced. Format strings and vendor token grammars
|
|
46
46
|
live in the parsers' profile systems, not here.
|
|
47
47
|
- **No two-digit years, ever.** `year` must be four digits. There is no century
|
|
48
48
|
window, no pivot, and no configuration that adds one.
|
|
49
|
-
- **No arithmetic, comparison or formatting helpers.** Convert with
|
|
49
|
+
- **No arithmetic, comparison, or formatting helpers.** Convert with
|
|
50
50
|
`toTemporal()` and use Temporal for the rest.
|
|
51
51
|
- **No locale-aware or human-readable output.** `toISO()` renders ISO 8601 and
|
|
52
52
|
nothing else.
|
|
@@ -59,8 +59,8 @@ Not covered at 0.1.0, and named so the boundary is not a surprise:
|
|
|
59
59
|
pnpm add @cosyte/dates
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Node
|
|
63
|
-
will not load it.
|
|
62
|
+
Node.js 22 or later, tested on 22, 24, and 26. ESM only: this package ships no
|
|
63
|
+
CommonJS build, so `require()` will not load it.
|
|
64
64
|
|
|
65
65
|
It carries exactly one runtime dependency, [`temporal-polyfill`][polyfill],
|
|
66
66
|
because `Temporal.PlainDate` is the only widely available type that can hold a
|
|
@@ -73,19 +73,19 @@ second copy.
|
|
|
73
73
|
## Usage
|
|
74
74
|
|
|
75
75
|
```js
|
|
76
|
-
import { precisionOf, toISO, toInstant, toTemporal, validateParts } from
|
|
76
|
+
import { precisionOf, toISO, toInstant, toTemporal, validateParts } from "@cosyte/dates";
|
|
77
77
|
|
|
78
78
|
// A date of birth that arrived as a year and nothing more. It stays a year.
|
|
79
79
|
const birthYear = { year: 1988 };
|
|
80
|
-
console.log(toISO(birthYear),
|
|
80
|
+
console.log(toISO(birthYear), "| precision:", precisionOf(birthYear));
|
|
81
81
|
|
|
82
82
|
// A calendar date. No midnight is invented, and no zone is attached.
|
|
83
83
|
const admitted = { year: 1988, month: 5, day: 7 };
|
|
84
|
-
console.log(toISO(admitted),
|
|
84
|
+
console.log(toISO(admitted), "| Temporal:", toTemporal(admitted).constructor.name);
|
|
85
85
|
|
|
86
86
|
// A timestamp whose sender stated its offset. The offset is kept exactly.
|
|
87
87
|
const collected = { year: 1988, month: 5, day: 7, hour: 13, minute: 45, offsetMinutes: 330 };
|
|
88
|
-
console.log(toISO(collected),
|
|
88
|
+
console.log(toISO(collected), "->", toInstant(collected).toString());
|
|
89
89
|
|
|
90
90
|
// The same timestamp with an unknown offset. Nothing guesses one.
|
|
91
91
|
const unknownZone = { year: 1988, month: 5, day: 7, hour: 13, minute: 45 };
|
|
@@ -97,7 +97,7 @@ try {
|
|
|
97
97
|
|
|
98
98
|
// A date that does not exist is refused, and says which rule it broke.
|
|
99
99
|
const result = validateParts({ year: 2025, month: 2, day: 29 });
|
|
100
|
-
console.log(result.valid ?
|
|
100
|
+
console.log(result.valid ? "valid" : result.issues[0].message);
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
```text
|
|
@@ -129,14 +129,14 @@ package does about that:
|
|
|
129
129
|
public entry point in a child process and asserting both streams are empty and
|
|
130
130
|
that nothing was written to the working directory.
|
|
131
131
|
- **It retains nothing.** Every function is pure and holds no state between
|
|
132
|
-
calls. A value passed in is not cached,
|
|
132
|
+
calls. A value passed in is not cached, memoized, or referenced after return.
|
|
133
133
|
- **It refuses rather than guesses.** No century window, no host timezone
|
|
134
134
|
fallback, no midnight for a date with no time, no picking one side of a
|
|
135
135
|
daylight-saving transition, no rounding a sub-second value to make it fit.
|
|
136
136
|
Each of those raises a named error instead.
|
|
137
137
|
- **Diagnostics quote one component, never the whole value.** A validation
|
|
138
138
|
message names the component at fault and the value of THAT component
|
|
139
|
-
(`month must be an integer in 1
|
|
139
|
+
(`month must be an integer in 1 to 12 ...; received the number 13`). It never
|
|
140
140
|
assembles the full date into an error string.
|
|
141
141
|
|
|
142
142
|
What the consumer still owns: whether to log an error message that may contain
|
|
@@ -149,7 +149,7 @@ the converted value goes next. This package is a pure function in the middle.
|
|
|
149
149
|
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
150
150
|
| `validateParts(value)` | Checks a value against the contract. Never throws. Returns every fault, not just the first. |
|
|
151
151
|
| `isValidParts(value)` | The same check as a type guard. |
|
|
152
|
-
| `assertValidParts(value)` | Returns the
|
|
152
|
+
| `assertValidParts(value)` | Returns the recognized components, or throws `DatePartsError`. |
|
|
153
153
|
| `precisionOf(value)` | The least significant present component: `'year'` through `'fraction'`. |
|
|
154
154
|
| `toISO(value)` | ISO 8601 at the value's own precision. `1988`, `1988-05`, `1988-05-07T13:45+05:30`. |
|
|
155
155
|
| `toTemporal(value)` | The narrowest Temporal type that carries everything the value states: `PlainYearMonth`, `PlainDate`, `PlainDateTime`, or `ZonedDateTime` when the value carries an offset. |
|
|
@@ -188,15 +188,29 @@ than one nanosecond, an offset on a value that states no time of day, and any
|
|
|
188
188
|
zone abbreviation such as `CST` (they are ambiguous across regions, so an IANA
|
|
189
189
|
identifier or a numeric offset is required).
|
|
190
190
|
|
|
191
|
+
## Support
|
|
192
|
+
|
|
193
|
+
Questions and bug reports go in [GitHub issues](https://github.com/cosyte/dates/issues). Use
|
|
194
|
+
synthetic values only: a date of birth or an admission time from a real patient is PHI, so reduce a
|
|
195
|
+
report to made-up parts that reproduce it. For help with an integration, see our
|
|
196
|
+
[integration services](https://cosyte.com/integration-services).
|
|
197
|
+
|
|
198
|
+
## Security
|
|
199
|
+
|
|
200
|
+
Report a vulnerability privately, never in a public issue, and with no real patient data in it. Our
|
|
201
|
+
[security policy](https://github.com/cosyte/dates/security/policy) says how. A conversion that
|
|
202
|
+
silently moves a value, rather than refusing, is worth reporting that way.
|
|
203
|
+
|
|
191
204
|
## Contributing
|
|
192
205
|
|
|
193
206
|
Issues and pull requests are welcome at
|
|
194
207
|
[github.com/cosyte/dates](https://github.com/cosyte/dates). A contribution has
|
|
195
|
-
to clear the same gate CI runs on every push
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
touching conversion needs one that pins the exact output rather than a shape.
|
|
208
|
+
to clear the same gate CI runs on every push; [`AGENTS.md`](https://github.com/cosyte/dates/blob/main/AGENTS.md)
|
|
209
|
+
lists the commands. New behavior needs a test, and anything touching conversion
|
|
210
|
+
needs one that pins the exact output rather than a shape.
|
|
199
211
|
|
|
200
212
|
## License
|
|
201
213
|
|
|
202
|
-
MIT, Cosyte. See [LICENSE](https://github.com/cosyte/dates/blob/main/LICENSE).
|
|
214
|
+
MIT, copyright Cosyte LLC. See [LICENSE](https://github.com/cosyte/dates/blob/main/LICENSE). The
|
|
215
|
+
standards this package names belong to their owners; see
|
|
216
|
+
[TRADEMARKS.md](https://github.com/cosyte/dates/blob/main/TRADEMARKS.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -14,20 +14,20 @@ export { Temporal } from 'temporal-polyfill';
|
|
|
14
14
|
* supplied, and an absent `offsetMinutes` means the offset is UNKNOWN, which
|
|
15
15
|
* is not the same as UTC and not the same as the host zone.
|
|
16
16
|
*
|
|
17
|
-
* `month` is SPEC-NATIVE 1
|
|
17
|
+
* `month` is SPEC-NATIVE 1 to 12, not JavaScript's 0 to 11.
|
|
18
18
|
*/
|
|
19
19
|
interface DateParts {
|
|
20
|
-
/** Four digits, 1000
|
|
20
|
+
/** Four digits, 1000 to 9999. Required. Two-digit years are never interpreted. */
|
|
21
21
|
readonly year?: number;
|
|
22
|
-
/** 1
|
|
22
|
+
/** 1 to 12, spec-native. January is 1. */
|
|
23
23
|
readonly month?: number;
|
|
24
|
-
/** 1
|
|
24
|
+
/** 1 to 31, and a day that exists in the given month of the given year. */
|
|
25
25
|
readonly day?: number;
|
|
26
|
-
/** 0
|
|
26
|
+
/** 0 to 23. */
|
|
27
27
|
readonly hour?: number;
|
|
28
|
-
/** 0
|
|
28
|
+
/** 0 to 59. */
|
|
29
29
|
readonly minute?: number;
|
|
30
|
-
/** 0
|
|
30
|
+
/** 0 to 59. Leap seconds are not representable. */
|
|
31
31
|
readonly second?: number;
|
|
32
32
|
/** Sub-second precision as a fraction of one second: `0 <= fraction < 1`. */
|
|
33
33
|
readonly fraction?: number;
|
|
@@ -42,35 +42,35 @@ declare const PRECISION_LADDER: readonly ["year", "month", "day", "hour", "minut
|
|
|
42
42
|
/** The least significant component present in a value IS its precision. */
|
|
43
43
|
type Precision = (typeof PRECISION_LADDER)[number];
|
|
44
44
|
/** Every component name the contract defines. */
|
|
45
|
-
type PartName = Precision |
|
|
45
|
+
type PartName = Precision | "offsetMinutes";
|
|
46
46
|
/** What a diagnostic says went wrong. */
|
|
47
47
|
type IssueCode =
|
|
48
48
|
/** The argument is not a date-parts object at all. */
|
|
49
|
-
|
|
49
|
+
"not-a-parts-object"
|
|
50
50
|
/** A more significant component is absent while a less significant one is present. */
|
|
51
|
-
|
|
|
51
|
+
| "missing-component"
|
|
52
52
|
/** Reading the component threw, so its value could not be seen at all. */
|
|
53
|
-
|
|
|
53
|
+
| "unreadable"
|
|
54
54
|
/** The component is present but is not a number. */
|
|
55
|
-
|
|
|
55
|
+
| "not-a-number"
|
|
56
56
|
/** The component is a number but is `NaN` or infinite. */
|
|
57
|
-
|
|
|
57
|
+
| "not-finite"
|
|
58
58
|
/** The component must be a whole number and is not. */
|
|
59
|
-
|
|
|
59
|
+
| "not-an-integer"
|
|
60
60
|
/** The component is outside the range the contract gives it. */
|
|
61
|
-
|
|
|
61
|
+
| "out-of-range"
|
|
62
62
|
/** The year is not four digits. No century window is ever applied. */
|
|
63
|
-
|
|
|
63
|
+
| "year-not-four-digits"
|
|
64
64
|
/** The sub-second value carries detail below nanosecond resolution. */
|
|
65
|
-
|
|
|
65
|
+
| "finer-than-nanosecond"
|
|
66
66
|
/** An offset was given for a value that states no time of day. */
|
|
67
|
-
|
|
|
67
|
+
| "offset-without-time-of-day"
|
|
68
68
|
/** The components are each in range but together denote no real date. */
|
|
69
|
-
|
|
|
69
|
+
| "no-such-calendar-date";
|
|
70
70
|
/** One thing wrong with one component, named. */
|
|
71
71
|
interface ValidationIssue {
|
|
72
72
|
/** The component at fault, or `'value'` when the argument itself is wrong. */
|
|
73
|
-
readonly component: PartName |
|
|
73
|
+
readonly component: PartName | "value";
|
|
74
74
|
readonly code: IssueCode;
|
|
75
75
|
/** Human-readable, names the component and the range or calendar rule broken. */
|
|
76
76
|
readonly message: string;
|
|
@@ -86,6 +86,7 @@ interface InvalidPartsResult {
|
|
|
86
86
|
readonly valid: false;
|
|
87
87
|
readonly issues: readonly ValidationIssue[];
|
|
88
88
|
}
|
|
89
|
+
/** What `validateParts()` returns: a valid value with its precision, or every fault found. */
|
|
89
90
|
type ValidationResult = ValidPartsResult | InvalidPartsResult;
|
|
90
91
|
/**
|
|
91
92
|
* How to place a value that carries no offset of its own onto the timeline.
|
|
@@ -106,9 +107,19 @@ interface ZoneOptions {
|
|
|
106
107
|
* `issues` is the SAME array `validateParts()` returns for that value, so a
|
|
107
108
|
* caller that catches this never has to validate again to learn what was wrong,
|
|
108
109
|
* and a conversion never returns a coerced or clamped stand-in.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* try {
|
|
114
|
+
* toISO({ year: 2025, month: 2, day: 29 });
|
|
115
|
+
* } catch (error) {
|
|
116
|
+
* if (error instanceof DatePartsError) error.issues[0]?.code; // 'no-such-calendar-date'
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
109
119
|
*/
|
|
110
120
|
declare class DatePartsError extends Error {
|
|
111
121
|
readonly issues: readonly ValidationIssue[];
|
|
122
|
+
/** @param issues - Every fault `validateParts()` found in the value. */
|
|
112
123
|
constructor(issues: readonly ValidationIssue[]);
|
|
113
124
|
}
|
|
114
125
|
/**
|
|
@@ -117,17 +128,36 @@ declare class DatePartsError extends Error {
|
|
|
117
128
|
*
|
|
118
129
|
* This package never falls back to the host timezone, to UTC, or to any other
|
|
119
130
|
* default, because a fallback turns an unknown into a plausible wrong answer.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* toInstant({ year: 2026, month: 3, day: 9, hour: 8, minute: 5 }); // throws MissingZoneError
|
|
135
|
+
* ```
|
|
120
136
|
*/
|
|
121
137
|
declare class MissingZoneError extends Error {
|
|
138
|
+
/** @param message - What was asked for, and how to say which zone applies. */
|
|
122
139
|
constructor(message: string);
|
|
123
140
|
}
|
|
124
141
|
/**
|
|
125
142
|
* The value does not carry enough precision for what was asked, and supplying
|
|
126
143
|
* the missing components would mean inventing them.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* try {
|
|
148
|
+
* toTemporal({ year: 1988 });
|
|
149
|
+
* } catch (error) {
|
|
150
|
+
* if (error instanceof PrecisionError) error.precision; // 'year'
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
127
153
|
*/
|
|
128
154
|
declare class PrecisionError extends Error {
|
|
129
155
|
/** The precision the value actually has. */
|
|
130
156
|
readonly precision: string;
|
|
157
|
+
/**
|
|
158
|
+
* @param message - What was asked for, and which components it needs.
|
|
159
|
+
* @param precision - The precision the value actually has.
|
|
160
|
+
*/
|
|
131
161
|
constructor(message: string, precision: string);
|
|
132
162
|
}
|
|
133
163
|
/**
|
|
@@ -137,8 +167,16 @@ declare class PrecisionError extends Error {
|
|
|
137
167
|
* Both cases are resolved by REFUSING rather than by picking. A daylight-saving
|
|
138
168
|
* transition is exactly where a silent choice shifts a clinical timeline by an
|
|
139
169
|
* hour, so the caller is told and decides.
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```ts
|
|
173
|
+
* const local = { year: 2026, month: 11, day: 1, hour: 1, minute: 30 };
|
|
174
|
+
* toInstant(local, { timeZone: "America/New_York" });
|
|
175
|
+
* // throws AmbiguousLocalTimeError: 2026-11-01T01:30:00 happens twice in America/New_York ...
|
|
176
|
+
* ```
|
|
140
177
|
*/
|
|
141
178
|
declare class AmbiguousLocalTimeError extends Error {
|
|
179
|
+
/** @param message - The local time, the zone, and the candidate offsets where there are two. */
|
|
142
180
|
constructor(message: string);
|
|
143
181
|
}
|
|
144
182
|
|
|
@@ -157,13 +195,36 @@ declare class AmbiguousLocalTimeError extends Error {
|
|
|
157
195
|
* Components are looked up by name, so an object carrying them on its prototype
|
|
158
196
|
* (a class instance with accessors, which is a shape a parser may well hand
|
|
159
197
|
* over) is read the same way a plain object literal is.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* validateParts({ year: 1988, month: 5, day: 7 });
|
|
202
|
+
* // { valid: true, parts: { year: 1988, month: 5, day: 7 }, precision: 'day' }
|
|
203
|
+
*
|
|
204
|
+
* const result = validateParts({ year: 1988, month: 13, day: 40 });
|
|
205
|
+
* result.valid; // false
|
|
206
|
+
* ```
|
|
160
207
|
*/
|
|
161
208
|
declare function validateParts(value: unknown): ValidationResult;
|
|
162
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* A type guard over {@link validateParts}, for callers that want a boolean.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* isValidParts({ year: 2024, month: 2, day: 29 }); // true
|
|
215
|
+
* isValidParts({ year: 2025, month: 2, day: 29 }); // false
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
163
218
|
declare function isValidParts(value: unknown): value is DateParts;
|
|
164
219
|
/**
|
|
165
220
|
* Validate and return the recognised components, or throw {@link DatePartsError}
|
|
166
221
|
* carrying the same diagnostics {@link validateParts} would have reported.
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* assertValidParts({ year: 1988, month: 5 }); // { year: 1988, month: 5 }
|
|
226
|
+
* assertValidParts({ year: 2025, month: 2, day: 29 }); // throws DatePartsError
|
|
227
|
+
* ```
|
|
167
228
|
*/
|
|
168
229
|
declare function assertValidParts(value: unknown): DateParts;
|
|
169
230
|
/**
|
|
@@ -171,6 +232,12 @@ declare function assertValidParts(value: unknown): DateParts;
|
|
|
171
232
|
*
|
|
172
233
|
* Throws {@link DatePartsError} for a value that does not satisfy the contract,
|
|
173
234
|
* because an invalid value has no precision to report.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* precisionOf({ year: 1988 }); // 'year'
|
|
239
|
+
* precisionOf({ year: 1988, month: 5, day: 7, hour: 13 }); // 'hour'
|
|
240
|
+
* ```
|
|
174
241
|
*/
|
|
175
242
|
declare function precisionOf(value: unknown): Precision;
|
|
176
243
|
|
|
@@ -186,6 +253,14 @@ type TemporalValue = Temporal.PlainYearMonth | Temporal.PlainDate | Temporal.Pla
|
|
|
186
253
|
* The result is a pure function of the parts. It does not read the host clock or
|
|
187
254
|
* the host time zone, so it is identical under every `TZ`.
|
|
188
255
|
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* toISO({ year: 1988 }); // '1988'
|
|
259
|
+
* toISO({ year: 2026, month: 3, day: 9, hour: 8 }); // '2026-03-09T08'
|
|
260
|
+
* toISO({ year: 2026, month: 3, day: 9, hour: 8, minute: 5, offsetMinutes: -300 });
|
|
261
|
+
* // '2026-03-09T08:05-05:00'
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
189
264
|
* @throws {DatePartsError} if the value does not satisfy the contract.
|
|
190
265
|
*/
|
|
191
266
|
declare function toISO(value: unknown): string;
|
|
@@ -209,6 +284,14 @@ declare function toISO(value: unknown): string;
|
|
|
209
284
|
* precision is still available from {@link precisionOf}; it is not lost, it just
|
|
210
285
|
* cannot be carried by the returned object.
|
|
211
286
|
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```ts
|
|
289
|
+
* toTemporal({ year: 2026, month: 3 }).toString(); // '2026-03'
|
|
290
|
+
* toTemporal({ year: 2026, month: 3, day: 9 }).toString(); // '2026-03-09'
|
|
291
|
+
* toTemporal({ year: 2026, month: 3, day: 9, hour: 8, minute: 5 }).toString();
|
|
292
|
+
* // '2026-03-09T08:05:00'
|
|
293
|
+
* ```
|
|
294
|
+
*
|
|
212
295
|
* @throws {DatePartsError} if the value does not satisfy the contract.
|
|
213
296
|
* @throws {PrecisionError} for a year-only value.
|
|
214
297
|
*/
|
|
@@ -233,6 +316,12 @@ declare function toTemporal(value: unknown): TemporalValue;
|
|
|
233
316
|
* @throws {PrecisionError} if the value states no time of day.
|
|
234
317
|
* @throws {MissingZoneError} if nothing says which zone applies.
|
|
235
318
|
* @throws {AmbiguousLocalTimeError} at a daylight-saving transition.
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* const local = { year: 2026, month: 3, day: 9, hour: 8, minute: 5 };
|
|
322
|
+
* toZonedDateTime(local, { timeZone: "America/New_York" }).toString();
|
|
323
|
+
* // '2026-03-09T08:05:00-04:00[America/New_York]'
|
|
324
|
+
* ```
|
|
236
325
|
*/
|
|
237
326
|
declare function toZonedDateTime(value: unknown, options?: ZoneOptions): Temporal.ZonedDateTime;
|
|
238
327
|
/**
|
|
@@ -240,6 +329,13 @@ declare function toZonedDateTime(value: unknown, options?: ZoneOptions): Tempora
|
|
|
240
329
|
*
|
|
241
330
|
* Same requirements as {@link toZonedDateTime}: a time of day, and a zone that
|
|
242
331
|
* something states. Nothing is defaulted.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* const local = { year: 2026, month: 3, day: 9, hour: 8, minute: 5 };
|
|
336
|
+
* toInstant(local, { timeZone: "America/New_York" }).toString(); // '2026-03-09T12:05:00Z'
|
|
337
|
+
* toInstant({ ...local, offsetMinutes: -300 }).toString(); // '2026-03-09T13:05:00Z'
|
|
338
|
+
* ```
|
|
243
339
|
*/
|
|
244
340
|
declare function toInstant(value: unknown, options?: ZoneOptions): Temporal.Instant;
|
|
245
341
|
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var PRECISION_LADDER = [
|
|
|
15
15
|
// src/errors.ts
|
|
16
16
|
var DatePartsError = class extends Error {
|
|
17
17
|
issues;
|
|
18
|
+
/** @param issues - Every fault `validateParts()` found in the value. */
|
|
18
19
|
constructor(issues) {
|
|
19
20
|
super(`invalid date parts: ${issues.map((issue) => issue.message).join("; ")}`);
|
|
20
21
|
this.name = "DatePartsError";
|
|
@@ -22,6 +23,7 @@ var DatePartsError = class extends Error {
|
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
25
|
var MissingZoneError = class extends Error {
|
|
26
|
+
/** @param message - What was asked for, and how to say which zone applies. */
|
|
25
27
|
constructor(message) {
|
|
26
28
|
super(message);
|
|
27
29
|
this.name = "MissingZoneError";
|
|
@@ -30,6 +32,10 @@ var MissingZoneError = class extends Error {
|
|
|
30
32
|
var PrecisionError = class extends Error {
|
|
31
33
|
/** The precision the value actually has. */
|
|
32
34
|
precision;
|
|
35
|
+
/**
|
|
36
|
+
* @param message - What was asked for, and which components it needs.
|
|
37
|
+
* @param precision - The precision the value actually has.
|
|
38
|
+
*/
|
|
33
39
|
constructor(message, precision) {
|
|
34
40
|
super(message);
|
|
35
41
|
this.name = "PrecisionError";
|
|
@@ -37,6 +43,7 @@ var PrecisionError = class extends Error {
|
|
|
37
43
|
}
|
|
38
44
|
};
|
|
39
45
|
var AmbiguousLocalTimeError = class extends Error {
|
|
46
|
+
/** @param message - The local time, the zone, and the candidate offsets where there are two. */
|
|
40
47
|
constructor(message) {
|
|
41
48
|
super(message);
|
|
42
49
|
this.name = "AmbiguousLocalTimeError";
|
|
@@ -239,11 +246,11 @@ function validateParts(value) {
|
|
|
239
246
|
name === "year" ? {
|
|
240
247
|
component: "year",
|
|
241
248
|
code: "year-not-four-digits",
|
|
242
|
-
message: `year must be a four-digit year in ${String(low)}
|
|
249
|
+
message: `year must be a four-digit year in ${String(low)} to ${String(high)}; received the number ${String(raw)}. This package applies no century window and never interprets a two-digit year`
|
|
243
250
|
} : {
|
|
244
251
|
component: name,
|
|
245
252
|
code: "out-of-range",
|
|
246
|
-
message: `${name} must be an integer in ${String(low)}
|
|
253
|
+
message: `${name} must be an integer in ${String(low)} to ${String(high)}${RANGE_NOTES[name] ?? ""}; received the number ${String(raw)}`
|
|
247
254
|
}
|
|
248
255
|
);
|
|
249
256
|
continue;
|
|
@@ -420,7 +427,7 @@ function assertKnownTimeZone(timeZone) {
|
|
|
420
427
|
function assertUsableOffset(offsetMinutes) {
|
|
421
428
|
if (!Number.isInteger(offsetMinutes) || offsetMinutes < -1439 || offsetMinutes > 1439) {
|
|
422
429
|
throw new RangeError(
|
|
423
|
-
`offsetMinutes must be an integer in -1439
|
|
430
|
+
`offsetMinutes must be an integer in -1439 to 1439 (signed minutes from UTC); received ${String(offsetMinutes)}`
|
|
424
431
|
);
|
|
425
432
|
}
|
|
426
433
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/fraction.ts","../src/validate.ts","../src/convert.ts"],"names":["nanosecondsOf"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The date-parts value every `@cosyte/*` parser hands back, and the only shape\n * this package accepts.\n *\n * Two rules make it a contract rather than a bag of numbers.\n *\n * 1. CONTIGUITY. Components are present from the most significant downward with\n * no gaps. `{ year, month }` is legal; `{ year, day }` is not.\n * 2. PRECISION IS WHAT IS PRESENT. The least significant present component IS\n * the precision of the value. Nothing here adds a component that was not\n * supplied, and an absent `offsetMinutes` means the offset is UNKNOWN, which\n * is not the same as UTC and not the same as the host zone.\n *\n * `month` is SPEC-NATIVE 1-12, not JavaScript's 0-11.\n */\nexport interface DateParts {\n /** Four digits, 1000-9999. Required. Two-digit years are never interpreted. */\n readonly year?: number;\n /** 1-12, spec-native. January is 1. */\n readonly month?: number;\n /** 1-31, and a day that exists in the given month of the given year. */\n readonly day?: number;\n /** 0-23. */\n readonly hour?: number;\n /** 0-59. */\n readonly minute?: number;\n /** 0-59. Leap seconds are not representable. */\n readonly second?: number;\n /** Sub-second precision as a fraction of one second: `0 <= fraction < 1`. */\n readonly fraction?: number;\n /**\n * Signed minutes from UTC, -1439 to 1439. ABSENT means the offset is unknown.\n * It never means zero, and it is never filled in from the host zone.\n */\n readonly offsetMinutes?: number;\n}\n\n/** The precision ladder, most significant first. */\nexport const PRECISION_LADDER = [\n 'year',\n 'month',\n 'day',\n 'hour',\n 'minute',\n 'second',\n 'fraction',\n] as const;\n\n/** The least significant component present in a value IS its precision. */\nexport type Precision = (typeof PRECISION_LADDER)[number];\n\n/** Every component name the contract defines. */\nexport type PartName = Precision | 'offsetMinutes';\n\n/** What a diagnostic says went wrong. */\nexport type IssueCode =\n /** The argument is not a date-parts object at all. */\n | 'not-a-parts-object'\n /** A more significant component is absent while a less significant one is present. */\n | 'missing-component'\n /** Reading the component threw, so its value could not be seen at all. */\n | 'unreadable'\n /** The component is present but is not a number. */\n | 'not-a-number'\n /** The component is a number but is `NaN` or infinite. */\n | 'not-finite'\n /** The component must be a whole number and is not. */\n | 'not-an-integer'\n /** The component is outside the range the contract gives it. */\n | 'out-of-range'\n /** The year is not four digits. No century window is ever applied. */\n | 'year-not-four-digits'\n /** The sub-second value carries detail below nanosecond resolution. */\n | 'finer-than-nanosecond'\n /** An offset was given for a value that states no time of day. */\n | 'offset-without-time-of-day'\n /** The components are each in range but together denote no real date. */\n | 'no-such-calendar-date';\n\n/** One thing wrong with one component, named. */\nexport interface ValidationIssue {\n /** The component at fault, or `'value'` when the argument itself is wrong. */\n readonly component: PartName | 'value';\n readonly code: IssueCode;\n /** Human-readable, names the component and the range or calendar rule broken. */\n readonly message: string;\n}\n\n/** A validated value, carrying only the recognised components that were present. */\nexport interface ValidPartsResult {\n readonly valid: true;\n readonly parts: DateParts;\n readonly precision: Precision;\n}\n\n/** An invalid value, with every fault found rather than only the first. */\nexport interface InvalidPartsResult {\n readonly valid: false;\n readonly issues: readonly ValidationIssue[];\n}\n\nexport type ValidationResult = ValidPartsResult | InvalidPartsResult;\n\n/**\n * How to place a value that carries no offset of its own onto the timeline.\n *\n * A value that DOES carry `offsetMinutes` is never re-zoned: its own offset is\n * what the sender said, and overriding it here would move the instant.\n */\nexport interface ZoneOptions {\n /** An IANA time zone identifier, for example `'America/New_York'`. */\n readonly timeZone?: string;\n /** A fixed offset in signed minutes from UTC, -1439 to 1439. */\n readonly offsetMinutes?: number;\n}\n","import type { ValidationIssue } from './types.js';\n\n/**\n * A conversion was asked for a value that validation reports invalid.\n *\n * `issues` is the SAME array `validateParts()` returns for that value, so a\n * caller that catches this never has to validate again to learn what was wrong,\n * and a conversion never returns a coerced or clamped stand-in.\n */\nexport class DatePartsError extends Error {\n readonly issues: readonly ValidationIssue[];\n\n constructor(issues: readonly ValidationIssue[]) {\n super(`invalid date parts: ${issues.map((issue) => issue.message).join('; ')}`);\n this.name = 'DatePartsError';\n this.issues = issues;\n }\n}\n\n/**\n * An absolute instant was asked for, and nothing said which zone the local time\n * is in.\n *\n * This package never falls back to the host timezone, to UTC, or to any other\n * default, because a fallback turns an unknown into a plausible wrong answer.\n */\nexport class MissingZoneError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MissingZoneError';\n }\n}\n\n/**\n * The value does not carry enough precision for what was asked, and supplying\n * the missing components would mean inventing them.\n */\nexport class PrecisionError extends Error {\n /** The precision the value actually has. */\n readonly precision: string;\n\n constructor(message: string, precision: string) {\n super(message);\n this.name = 'PrecisionError';\n this.precision = precision;\n }\n}\n\n/**\n * A local time was placed in a named zone where it does not exist, or exists\n * twice.\n *\n * Both cases are resolved by REFUSING rather than by picking. A daylight-saving\n * transition is exactly where a silent choice shifts a clinical timeline by an\n * hour, so the caller is told and decides.\n */\nexport class AmbiguousLocalTimeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AmbiguousLocalTimeError';\n }\n}\n","import type { ValidationIssue } from './types.js';\n\n/**\n * Reading a `fraction` as whole nanoseconds, EXACTLY.\n *\n * One function answers \"how many nanoseconds is this?\" for the whole package, so\n * the validator and every converter cannot disagree about a value: a fraction the\n * validator accepts is one every converter can render, and a fraction any of them\n * refuses is refused by all of them with the same diagnostic.\n *\n * The count is read from the value's own decimal digits rather than computed as\n * `fraction * 1e9`. Binary floating point cannot hold most decimal fractions, so\n * that product misses whole nanoseconds by a hair and any tolerance wide enough\n * to forgive the hair is also wide enough to swallow a real difference. Digits\n * are exact and need no tolerance at all.\n */\n\n/** Temporal's resolution floor, and this package's: nine decimal places. */\nconst NANOSECOND_DIGITS = 9;\n\n/** `0`, or `0.` followed by the sub-second digits. */\nconst PLAIN_DECIMAL = /^0(?:\\.(\\d+))?$/;\n\n/** JavaScript prints a magnitude below `1e-6` in exponential form: `1e-9`, `1.5e-7`. */\nconst EXPONENTIAL_DECIMAL = /^(\\d)(?:\\.(\\d+))?e-(\\d+)$/;\n\n/**\n * The digits after the decimal point of the shortest decimal that round-trips to\n * `fraction`, or `undefined` if the value has no such form.\n *\n * The shortest round-tripping decimal is the value's canonical spelling: it is\n * what `0.123456789` was written as, and every longer decimal that lands on the\n * same double is the same double.\n */\nfunction fractionDigits(fraction: number): string | undefined {\n const text = String(fraction);\n\n const plain = PLAIN_DECIMAL.exec(text);\n if (plain !== null) return plain[1] ?? '';\n\n const exponential = EXPONENTIAL_DECIMAL.exec(text);\n if (exponential === null) return undefined;\n\n const lead = exponential[1] ?? '';\n const rest = exponential[2] ?? '';\n const exponent = Number(exponential[3]);\n return '0'.repeat(exponent - 1) + lead + rest;\n}\n\n/**\n * The whole nanoseconds `fraction` denotes, or `undefined` when it carries detail\n * finer than one nanosecond.\n *\n * `fraction` must already be known finite and in `[0, 1)`; {@link readFraction}\n * is the entry point that establishes both. The result is therefore always in\n * `[0, 999999999]`, never a whole second.\n */\nexport function nanosecondsOf(fraction: number): number | undefined {\n const digits = fractionDigits(fraction);\n if (digits === undefined || digits.length > NANOSECOND_DIGITS) return undefined;\n return digits === '' ? 0 : Number(digits.padEnd(NANOSECOND_DIGITS, '0'));\n}\n\n/** A fraction read as whole nanoseconds, or the diagnostic that refuses it. */\nexport type FractionReading =\n | { readonly ok: true; readonly nanoseconds: number }\n | { readonly ok: false; readonly issue: ValidationIssue };\n\n/**\n * Check a `fraction` against the contract and read it as whole nanoseconds.\n *\n * Nothing is rounded. A value carrying detail below one nanosecond is REFUSED,\n * because rounding it away would silently change what the sender said, and this\n * package exists to not do that.\n */\nexport function readFraction(fraction: number): FractionReading {\n if (!Number.isFinite(fraction) || fraction < 0 || fraction >= 1) {\n return {\n ok: false,\n issue: {\n component: 'fraction',\n code: 'out-of-range',\n message: `fraction is a share of one second and must satisfy 0 <= fraction < 1; received the number ${String(fraction)}`,\n },\n };\n }\n\n const nanoseconds = nanosecondsOf(fraction);\n if (nanoseconds === undefined) {\n return {\n ok: false,\n issue: {\n component: 'fraction',\n code: 'finer-than-nanosecond',\n message: `fraction carries detail finer than one nanosecond, which this package will not round away; received the number ${String(fraction)}. One nanosecond is nine decimal places of a second, and it is the finest value Temporal can hold`,\n },\n };\n }\n\n return { ok: true, nanoseconds };\n}\n","import { DatePartsError } from './errors.js';\nimport { readFraction } from './fraction.js';\nimport { PRECISION_LADDER } from './types.js';\nimport type {\n DateParts,\n InvalidPartsResult,\n PartName,\n Precision,\n ValidationIssue,\n ValidationResult,\n} from './types.js';\n\nconst ALL_PARTS: readonly PartName[] = [...PRECISION_LADDER, 'offsetMinutes'];\n\ntype IntegerPart = Exclude<PartName, 'fraction'>;\n\nconst INTEGER_RANGES: Readonly<Record<IntegerPart, readonly [number, number]>> = {\n year: [1000, 9999],\n month: [1, 12],\n day: [1, 31],\n hour: [0, 23],\n minute: [0, 59],\n second: [0, 59],\n offsetMinutes: [-1439, 1439],\n};\n\nconst RANGE_NOTES: Readonly<Partial<Record<IntegerPart, string>>> = {\n month: ' (spec-native, January is 1, not 0)',\n offsetMinutes: ' (signed minutes from UTC)',\n};\n\nconst MONTH_NAMES = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n] as const;\n\nconst MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const;\n\n/** Proleptic Gregorian, applied to every year in range with no exceptions. */\nfunction isLeapYear(year: number): boolean {\n return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n}\n\nfunction daysInMonth(year: number, month: number): number {\n if (month === 2) return isLeapYear(year) ? 29 : 28;\n return MONTH_LENGTHS[month - 1] ?? 0;\n}\n\nfunction monthName(month: number): string {\n return MONTH_NAMES[month - 1] ?? `month ${String(month)}`;\n}\n\n/** A short, non-throwing description of a value, for diagnostics. */\nfunction describe(value: unknown): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (typeof value === 'number') {\n if (Number.isNaN(value)) return 'NaN';\n if (!Number.isFinite(value)) return value > 0 ? 'Infinity' : '-Infinity';\n return `the number ${String(value)}`;\n }\n if (typeof value === 'string') return 'a string';\n if (typeof value === 'boolean') return `the boolean ${String(value)}`;\n if (typeof value === 'bigint') return 'a bigint';\n if (typeof value === 'symbol') return 'a symbol';\n if (typeof value === 'function') return 'a function';\n if (Array.isArray(value)) return 'an array';\n return 'an object';\n}\n\nfunction invalid(issues: readonly ValidationIssue[]): InvalidPartsResult {\n return { valid: false, issues };\n}\n\n/**\n * Read one named component off a candidate parts object.\n *\n * A component can be an accessor, and an accessor can throw. Reading it is\n * therefore guarded, so that a hostile or merely broken object is REPORTED like\n * any other fault rather than raising out of a function whose whole contract is\n * that it answers instead of throwing.\n */\nfunction readComponent(\n source: Record<string, unknown>,\n name: PartName,\n):\n | { readonly ok: true; readonly raw: unknown }\n | { readonly ok: false; readonly issue: ValidationIssue } {\n try {\n return { ok: true, raw: source[name] };\n } catch {\n return {\n ok: false,\n issue: {\n component: name,\n code: 'unreadable',\n message: `${name} could not be read: the property threw when accessed, so this package cannot see what value it holds and will not guess one`,\n },\n };\n }\n}\n\n/**\n * Check a value against the date-parts contract.\n *\n * Every fault is reported, not only the first, and every diagnostic names the\n * component at fault together with the range or calendar rule it broke. Nothing\n * is coerced, clamped or inferred: a value that does not satisfy the contract\n * comes back invalid rather than corrected.\n *\n * This function never throws, for any argument at all. Hostile input (`null`, a\n * string, a JavaScript `Date`, `NaN`, `Infinity`, a non-integer component, a\n * component that throws when it is read) is reported, not raised.\n *\n * Components are looked up by name, so an object carrying them on its prototype\n * (a class instance with accessors, which is a shape a parser may well hand\n * over) is read the same way a plain object literal is.\n */\nexport function validateParts(value: unknown): ValidationResult {\n if (typeof value !== 'object' || value === null) {\n return invalid([\n {\n component: 'value',\n code: 'not-a-parts-object',\n message: `a date-parts value must be an object carrying the components the contract names; received ${describe(value)}`,\n },\n ]);\n }\n\n if (value instanceof Date) {\n return invalid([\n {\n component: 'value',\n code: 'not-a-parts-object',\n message:\n 'a JavaScript Date is an absolute instant, not a date-parts value: it cannot express a year-only or month-only date, and it carries no offset of its own. Pass the parts the parser returned',\n },\n ]);\n }\n\n if (Array.isArray(value)) {\n return invalid([\n {\n component: 'value',\n code: 'not-a-parts-object',\n message:\n 'a date-parts value must be an object with named components; received an array, whose positions this package will not guess at',\n },\n ]);\n }\n\n const source = value as Record<string, unknown>;\n const issues: ValidationIssue[] = [];\n const present = new Set<PartName>();\n const values: Partial<Record<PartName, number>> = {};\n\n for (const name of ALL_PARTS) {\n const read = readComponent(source, name);\n if (!read.ok) {\n present.add(name);\n issues.push(read.issue);\n continue;\n }\n\n const raw = read.raw;\n // An explicit `undefined` reads as absent, the way an optional property does.\n if (raw === undefined) continue;\n present.add(name);\n\n if (typeof raw !== 'number') {\n issues.push({\n component: name,\n code: 'not-a-number',\n message: `${name} must be a number; received ${describe(raw)}`,\n });\n continue;\n }\n\n if (!Number.isFinite(raw)) {\n issues.push({\n component: name,\n code: 'not-finite',\n message: `${name} must be a finite number; received ${describe(raw)}`,\n });\n continue;\n }\n\n if (name === 'fraction') {\n const reading = readFraction(raw);\n if (reading.ok) values.fraction = raw;\n else issues.push(reading.issue);\n continue;\n }\n\n if (!Number.isInteger(raw)) {\n issues.push({\n component: name,\n code: 'not-an-integer',\n message: `${name} must be a whole number; received the number ${String(raw)}`,\n });\n continue;\n }\n\n const [low, high] = INTEGER_RANGES[name];\n if (raw < low || raw > high) {\n issues.push(\n name === 'year'\n ? {\n component: 'year',\n code: 'year-not-four-digits',\n message: `year must be a four-digit year in ${String(low)}-${String(high)}; received the number ${String(raw)}. This package applies no century window and never interprets a two-digit year`,\n }\n : {\n component: name,\n code: 'out-of-range',\n message: `${name} must be an integer in ${String(low)}-${String(high)}${RANGE_NOTES[name] ?? ''}; received the number ${String(raw)}`,\n },\n );\n continue;\n }\n\n values[name] = raw;\n }\n\n checkContiguity(present, issues);\n checkOffsetHasTimeOfDay(present, issues);\n checkCalendarDate(values, issues);\n\n if (issues.length > 0) return invalid(issues);\n\n const parts: { -readonly [K in keyof DateParts]?: number } = {};\n for (const name of ALL_PARTS) {\n const held = values[name];\n if (held !== undefined) parts[name] = held;\n }\n\n return { valid: true, parts, precision: deepestPresent(present) as Precision };\n}\n\nfunction deepestPresent(present: ReadonlySet<PartName>): Precision | undefined {\n let deepest: Precision | undefined;\n for (const name of PRECISION_LADDER) {\n if (present.has(name)) deepest = name;\n }\n return deepest;\n}\n\nfunction checkContiguity(present: ReadonlySet<PartName>, issues: ValidationIssue[]): void {\n const deepest = deepestPresent(present);\n\n if (deepest === undefined) {\n issues.push({\n component: 'year',\n code: 'missing-component',\n message:\n 'year is missing: a date-parts value must carry at least a year, and this package never infers one',\n });\n return;\n }\n\n const deepestIndex = PRECISION_LADDER.indexOf(deepest);\n for (let index = 0; index < deepestIndex; index += 1) {\n const name = PRECISION_LADDER[index];\n if (name === undefined || present.has(name)) continue;\n issues.push({\n component: name,\n code: 'missing-component',\n message: `${name} is missing while ${deepest} is present: components run from year downward with no gaps, and this package never infers the one you left out`,\n });\n }\n}\n\nfunction checkOffsetHasTimeOfDay(present: ReadonlySet<PartName>, issues: ValidationIssue[]): void {\n if (!present.has('offsetMinutes') || present.has('hour')) return;\n issues.push({\n component: 'offsetMinutes',\n code: 'offset-without-time-of-day',\n message:\n 'offsetMinutes is present but the value states no time of day: an offset qualifies a clock time, it cannot be rendered without one, and dropping it silently is not an option this package takes',\n });\n}\n\nfunction checkCalendarDate(\n values: Partial<Record<PartName, number>>,\n issues: ValidationIssue[],\n): void {\n const { year, month, day } = values;\n if (year === undefined || month === undefined || day === undefined) return;\n\n const limit = daysInMonth(year, month);\n if (day <= limit) return;\n\n const leapNote =\n month === 2\n ? `, because ${String(year)} is ${isLeapYear(year) ? 'a' : 'not a'} leap year in the proleptic Gregorian calendar`\n : '';\n issues.push({\n component: 'day',\n code: 'no-such-calendar-date',\n message: `day ${String(day)} does not exist: ${monthName(month)} ${String(year)} has ${String(limit)} days${leapNote}`,\n });\n}\n\n/** A type guard over {@link validateParts}, for callers that want a boolean. */\nexport function isValidParts(value: unknown): value is DateParts {\n return validateParts(value).valid;\n}\n\n/**\n * Validate and return the recognised components, or throw {@link DatePartsError}\n * carrying the same diagnostics {@link validateParts} would have reported.\n */\nexport function assertValidParts(value: unknown): DateParts {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return result.parts;\n}\n\n/**\n * The precision of a value: its least significant present component.\n *\n * Throws {@link DatePartsError} for a value that does not satisfy the contract,\n * because an invalid value has no precision to report.\n */\nexport function precisionOf(value: unknown): Precision {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return result.precision;\n}\n","import { Temporal } from 'temporal-polyfill';\n\nimport {\n AmbiguousLocalTimeError,\n DatePartsError,\n MissingZoneError,\n PrecisionError,\n} from './errors.js';\nimport { readFraction } from './fraction.js';\nimport { PRECISION_LADDER } from './types.js';\nimport type { DateParts, Precision, ZoneOptions } from './types.js';\nimport { validateParts } from './validate.js';\n\n/** The narrowest Temporal type that carries everything a value states, and nothing more. */\nexport type TemporalValue =\n Temporal.PlainYearMonth | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime;\n\ninterface Checked {\n readonly parts: DateParts;\n readonly precision: Precision;\n}\n\nfunction check(value: unknown): Checked {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return { parts: result.parts, precision: result.precision };\n}\n\nfunction atLeast(precision: Precision, floor: Precision): boolean {\n return PRECISION_LADDER.indexOf(precision) >= PRECISION_LADDER.indexOf(floor);\n}\n\nfunction pad(value: number, width: number): string {\n return String(value).padStart(width, '0');\n}\n\n/**\n * Signed minutes rendered as `+HH:MM` or `-HH:MM`.\n *\n * A present offset of zero renders `+00:00` rather than `Z`, so the ISO string\n * says the same thing the parts do: an offset was stated, and it was zero. An\n * ABSENT offset renders nothing at all, because unknown is not UTC.\n */\nfunction formatOffset(offsetMinutes: number): string {\n const sign = offsetMinutes < 0 ? '-' : '+';\n const absolute = Math.abs(offsetMinutes);\n return `${sign}${pad(Math.trunc(absolute / 60), 2)}:${pad(absolute % 60, 2)}`;\n}\n\n/**\n * The whole nanoseconds a fraction denotes.\n *\n * Every converter reads a fraction through the SAME function the validator uses,\n * so a value the validator has just certified can never reach a converter that\n * disagrees with it. A fraction the reader refuses is refused here with the\n * validator's own diagnostic rather than by leaking whatever Temporal makes of\n * a nonsense field.\n *\n * @throws {DatePartsError} if the fraction is not a whole number of nanoseconds.\n */\nfunction nanosecondsOf(fraction: number): number {\n const reading = readFraction(fraction);\n if (!reading.ok) throw new DatePartsError([reading.issue]);\n return reading.nanoseconds;\n}\n\n/** Sub-second digits, trimmed of trailing zeros but never to nothing. */\nfunction formatFraction(fraction: number): string {\n const digits = pad(nanosecondsOf(fraction), 9).replace(/0+$/, '');\n return `.${digits === '' ? '0' : digits}`;\n}\n\nfunction subSecondFields(fraction: number | undefined): {\n millisecond?: number;\n microsecond?: number;\n nanosecond?: number;\n} {\n if (fraction === undefined) return {};\n const nanoseconds = nanosecondsOf(fraction);\n return {\n millisecond: Math.floor(nanoseconds / 1e6),\n microsecond: Math.floor((nanoseconds % 1e6) / 1e3),\n nanosecond: nanoseconds % 1e3,\n };\n}\n\n/**\n * Render a value as an ISO 8601 string AT ITS OWN PRECISION.\n *\n * A year-only value renders `1988`; a month-only value renders `1988-05`. No\n * component the value did not state is ever added, so nothing here turns a year\n * of birth into the first of January.\n *\n * The result is a pure function of the parts. It does not read the host clock or\n * the host time zone, so it is identical under every `TZ`.\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n */\nexport function toISO(value: unknown): string {\n const { parts, precision } = check(value);\n const suffix = parts.offsetMinutes === undefined ? '' : formatOffset(parts.offsetMinutes);\n\n let out = pad(parts.year ?? 0, 4);\n if (precision === 'year') return out;\n\n out += `-${pad(parts.month ?? 0, 2)}`;\n if (precision === 'month') return out;\n\n out += `-${pad(parts.day ?? 0, 2)}`;\n if (precision === 'day') return out;\n\n out += `T${pad(parts.hour ?? 0, 2)}`;\n if (precision === 'hour') return out + suffix;\n\n out += `:${pad(parts.minute ?? 0, 2)}`;\n if (precision === 'minute') return out + suffix;\n\n out += `:${pad(parts.second ?? 0, 2)}`;\n if (precision === 'second') return out + suffix;\n\n out += formatFraction(parts.fraction ?? 0);\n return out + suffix;\n}\n\n/**\n * Convert to the narrowest Temporal type that carries everything the value\n * states, and nothing it does not.\n *\n * | precision of the value | result |\n * |---|---|\n * | `month` | `Temporal.PlainYearMonth` |\n * | `day` | `Temporal.PlainDate`, with no time of day and no zone |\n * | `hour` and finer, no offset | `Temporal.PlainDateTime`, with no zone |\n * | `hour` and finer, with `offsetMinutes` | `Temporal.ZonedDateTime` at that exact offset |\n *\n * A year-only value has no Temporal counterpart and raises\n * {@link PrecisionError} rather than acquiring a month. Use {@link toISO} for\n * those, and {@link precisionOf} to branch.\n *\n * Components below the value's own precision are zero in the Temporal object,\n * because Temporal has no partial type below `PlainYearMonth`. The value's real\n * precision is still available from {@link precisionOf}; it is not lost, it just\n * cannot be carried by the returned object.\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n * @throws {PrecisionError} for a year-only value.\n */\nexport function toTemporal(value: unknown): TemporalValue {\n const { parts, precision } = check(value);\n\n if (precision === 'year') {\n throw new PrecisionError(\n 'Temporal has no year-only type, and this package will not invent a month to reach one. Use toISO() to render a year-precision value, or precisionOf() to branch before converting',\n precision,\n );\n }\n\n const year = parts.year ?? 0;\n const month = parts.month ?? 0;\n\n if (precision === 'month') {\n return Temporal.PlainYearMonth.from({ year, month }, { overflow: 'reject' });\n }\n\n const day = parts.day ?? 0;\n if (precision === 'day') {\n return Temporal.PlainDate.from({ year, month, day }, { overflow: 'reject' });\n }\n\n const fields = {\n year,\n month,\n day,\n hour: parts.hour ?? 0,\n minute: parts.minute ?? 0,\n second: parts.second ?? 0,\n ...subSecondFields(parts.fraction),\n };\n\n if (parts.offsetMinutes !== undefined) {\n return Temporal.ZonedDateTime.from(\n { ...fields, timeZone: formatOffset(parts.offsetMinutes) },\n { overflow: 'reject' },\n );\n }\n\n return Temporal.PlainDateTime.from(fields, { overflow: 'reject' });\n}\n\nfunction assertKnownTimeZone(timeZone: string): void {\n try {\n // A fixed epoch, so this probe never reads the host clock or the host zone.\n Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(timeZone);\n } catch {\n throw new RangeError(\n `unknown time zone ${JSON.stringify(timeZone)}: supply an IANA identifier such as \"America/New_York\", or an offset such as \"+05:30\"`,\n );\n }\n}\n\nfunction assertUsableOffset(offsetMinutes: number): void {\n if (!Number.isInteger(offsetMinutes) || offsetMinutes < -1439 || offsetMinutes > 1439) {\n throw new RangeError(\n `offsetMinutes must be an integer in -1439-1439 (signed minutes from UTC); received ${String(offsetMinutes)}`,\n );\n }\n}\n\n/**\n * Decide which zone places this value on the timeline, or refuse.\n *\n * The value's OWN `offsetMinutes` always wins. That is what the sender said, and\n * re-zoning it on a caller's say-so would move the instant. `options` therefore\n * apply only to a value whose offset is unknown, which is exactly the case they\n * exist for.\n */\nfunction resolveTimeZone(parts: DateParts, options: ZoneOptions | undefined): string {\n if (parts.offsetMinutes !== undefined) return formatOffset(parts.offsetMinutes);\n\n if (options?.timeZone !== undefined && options.offsetMinutes !== undefined) {\n throw new TypeError(\n 'supply either timeZone or offsetMinutes, not both: two answers to one question is a guess waiting to happen',\n );\n }\n\n if (options?.offsetMinutes !== undefined) {\n assertUsableOffset(options.offsetMinutes);\n return formatOffset(options.offsetMinutes);\n }\n\n if (options?.timeZone !== undefined) {\n assertKnownTimeZone(options.timeZone);\n return options.timeZone;\n }\n\n throw new MissingZoneError(\n 'this value carries no offsetMinutes, and no zone was supplied: an absolute instant needs a zone, and this package supplies none. Pass { timeZone } or { offsetMinutes }. It will not fall back to the host time zone, to UTC, or to any other default, because a fallback turns an unknown offset into a plausible wrong instant',\n );\n}\n\n/**\n * Place a value on the timeline in a stated zone.\n *\n * Requires a time of day. A day-precision value is a calendar date, and turning\n * one into a zoned instant means choosing midnight, which is a component the\n * value does not carry. Use {@link toTemporal} for those.\n *\n * Requires a zone. If the value carries `offsetMinutes` that offset is used and\n * PRESERVED EXACTLY, never normalised to UTC or to the host zone, and `options`\n * are not consulted. Otherwise `options.timeZone` or `options.offsetMinutes`\n * must say which zone the local time is in.\n *\n * A local time that a named zone skips or repeats across a daylight-saving\n * transition raises {@link AmbiguousLocalTimeError} rather than resolving to\n * one of the two candidates, because a silent pick shifts the value by an hour.\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n * @throws {PrecisionError} if the value states no time of day.\n * @throws {MissingZoneError} if nothing says which zone applies.\n * @throws {AmbiguousLocalTimeError} at a daylight-saving transition.\n */\nexport function toZonedDateTime(value: unknown, options?: ZoneOptions): Temporal.ZonedDateTime {\n const { parts, precision } = check(value);\n\n if (!atLeast(precision, 'hour')) {\n // A value below hour precision that also states no zone is missing BOTH, and\n // hearing about them one at a time is how a caller fixes one and comes back.\n // The precision is what is reported, because supplying a zone would not make\n // this value convertible, but the message says the zone is absent too, and\n // says as firmly as the zone refusal does that nothing will be defaulted.\n const alsoNoZone =\n parts.offsetMinutes === undefined &&\n options?.timeZone === undefined &&\n options?.offsetMinutes === undefined;\n\n throw new PrecisionError(\n `an absolute instant needs a time of day, and this value has ${precision} precision. Supplying one would mean inventing components the value does not carry. Use toTemporal() for the calendar value${\n alsoNoZone\n ? '. The zone is missing too: this value carries no offsetMinutes and no { timeZone } or { offsetMinutes } was supplied, and neither the host time zone nor UTC will be used in their place'\n : ''\n }`,\n precision,\n );\n }\n\n const timeZone = resolveTimeZone(parts, options);\n const local = Temporal.PlainDateTime.from(\n {\n year: parts.year ?? 0,\n month: parts.month ?? 0,\n day: parts.day ?? 0,\n hour: parts.hour ?? 0,\n minute: parts.minute ?? 0,\n second: parts.second ?? 0,\n ...subSecondFields(parts.fraction),\n },\n { overflow: 'reject' },\n );\n\n // Ambiguity is established by ASKING, not by catching. The two disambiguation\n // rules agree on every local time a zone maps exactly once, and disagree on\n // exactly the times a transition skips or repeats. Reading the answer off a\n // thrown message would instead relabel any other failure as a transition, and\n // a fixed offset zone, which has no transitions at all, would be told it did.\n const earliest = local.toZonedDateTime(timeZone, { disambiguation: 'earlier' });\n const latest = local.toZonedDateTime(timeZone, { disambiguation: 'later' });\n\n if (!earliest.equals(latest)) {\n // A repeated local time still reads back as itself, at two offsets. A skipped\n // one cannot: both rules move it off the gap, so the wall clock shifts.\n const skipped = !earliest.toPlainDateTime().equals(local);\n throw new AmbiguousLocalTimeError(\n skipped\n ? `${local.toString()} does not exist in ${timeZone}: a daylight-saving transition skips it, so there is no instant to return. Say which side of the transition you mean by passing that offset as { offsetMinutes }`\n : `${local.toString()} happens twice in ${timeZone}: a daylight-saving transition repeats it, at ${earliest.offset} and again at ${latest.offset}, so there is no single instant to return. Choose the one you mean and pass it as { offsetMinutes }`,\n );\n }\n\n return earliest;\n}\n\n/**\n * The absolute instant a value denotes, in a stated zone.\n *\n * Same requirements as {@link toZonedDateTime}: a time of day, and a zone that\n * something states. Nothing is defaulted.\n */\nexport function toInstant(value: unknown, options?: ZoneOptions): Temporal.Instant {\n return toZonedDateTime(value, options).toInstant();\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/fraction.ts","../src/validate.ts","../src/convert.ts"],"names":["nanosecondsOf"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The date-parts value every `@cosyte/*` parser hands back, and the only shape\n * this package accepts.\n *\n * Two rules make it a contract rather than a bag of numbers.\n *\n * 1. CONTIGUITY. Components are present from the most significant downward with\n * no gaps. `{ year, month }` is legal; `{ year, day }` is not.\n * 2. PRECISION IS WHAT IS PRESENT. The least significant present component IS\n * the precision of the value. Nothing here adds a component that was not\n * supplied, and an absent `offsetMinutes` means the offset is UNKNOWN, which\n * is not the same as UTC and not the same as the host zone.\n *\n * `month` is SPEC-NATIVE 1 to 12, not JavaScript's 0 to 11.\n */\nexport interface DateParts {\n /** Four digits, 1000 to 9999. Required. Two-digit years are never interpreted. */\n readonly year?: number;\n /** 1 to 12, spec-native. January is 1. */\n readonly month?: number;\n /** 1 to 31, and a day that exists in the given month of the given year. */\n readonly day?: number;\n /** 0 to 23. */\n readonly hour?: number;\n /** 0 to 59. */\n readonly minute?: number;\n /** 0 to 59. Leap seconds are not representable. */\n readonly second?: number;\n /** Sub-second precision as a fraction of one second: `0 <= fraction < 1`. */\n readonly fraction?: number;\n /**\n * Signed minutes from UTC, -1439 to 1439. ABSENT means the offset is unknown.\n * It never means zero, and it is never filled in from the host zone.\n */\n readonly offsetMinutes?: number;\n}\n\n/** The precision ladder, most significant first. */\nexport const PRECISION_LADDER = [\n \"year\",\n \"month\",\n \"day\",\n \"hour\",\n \"minute\",\n \"second\",\n \"fraction\",\n] as const;\n\n/** The least significant component present in a value IS its precision. */\nexport type Precision = (typeof PRECISION_LADDER)[number];\n\n/** Every component name the contract defines. */\nexport type PartName = Precision | \"offsetMinutes\";\n\n/** What a diagnostic says went wrong. */\nexport type IssueCode =\n /** The argument is not a date-parts object at all. */\n | \"not-a-parts-object\"\n /** A more significant component is absent while a less significant one is present. */\n | \"missing-component\"\n /** Reading the component threw, so its value could not be seen at all. */\n | \"unreadable\"\n /** The component is present but is not a number. */\n | \"not-a-number\"\n /** The component is a number but is `NaN` or infinite. */\n | \"not-finite\"\n /** The component must be a whole number and is not. */\n | \"not-an-integer\"\n /** The component is outside the range the contract gives it. */\n | \"out-of-range\"\n /** The year is not four digits. No century window is ever applied. */\n | \"year-not-four-digits\"\n /** The sub-second value carries detail below nanosecond resolution. */\n | \"finer-than-nanosecond\"\n /** An offset was given for a value that states no time of day. */\n | \"offset-without-time-of-day\"\n /** The components are each in range but together denote no real date. */\n | \"no-such-calendar-date\";\n\n/** One thing wrong with one component, named. */\nexport interface ValidationIssue {\n /** The component at fault, or `'value'` when the argument itself is wrong. */\n readonly component: PartName | \"value\";\n readonly code: IssueCode;\n /** Human-readable, names the component and the range or calendar rule broken. */\n readonly message: string;\n}\n\n/** A validated value, carrying only the recognised components that were present. */\nexport interface ValidPartsResult {\n readonly valid: true;\n readonly parts: DateParts;\n readonly precision: Precision;\n}\n\n/** An invalid value, with every fault found rather than only the first. */\nexport interface InvalidPartsResult {\n readonly valid: false;\n readonly issues: readonly ValidationIssue[];\n}\n\n/** What `validateParts()` returns: a valid value with its precision, or every fault found. */\nexport type ValidationResult = ValidPartsResult | InvalidPartsResult;\n\n/**\n * How to place a value that carries no offset of its own onto the timeline.\n *\n * A value that DOES carry `offsetMinutes` is never re-zoned: its own offset is\n * what the sender said, and overriding it here would move the instant.\n */\nexport interface ZoneOptions {\n /** An IANA time zone identifier, for example `'America/New_York'`. */\n readonly timeZone?: string;\n /** A fixed offset in signed minutes from UTC, -1439 to 1439. */\n readonly offsetMinutes?: number;\n}\n","import type { ValidationIssue } from \"./types.js\";\n\n/**\n * A conversion was asked for a value that validation reports invalid.\n *\n * `issues` is the SAME array `validateParts()` returns for that value, so a\n * caller that catches this never has to validate again to learn what was wrong,\n * and a conversion never returns a coerced or clamped stand-in.\n *\n * @example\n * ```ts\n * try {\n * toISO({ year: 2025, month: 2, day: 29 });\n * } catch (error) {\n * if (error instanceof DatePartsError) error.issues[0]?.code; // 'no-such-calendar-date'\n * }\n * ```\n */\nexport class DatePartsError extends Error {\n readonly issues: readonly ValidationIssue[];\n\n /** @param issues - Every fault `validateParts()` found in the value. */\n constructor(issues: readonly ValidationIssue[]) {\n super(`invalid date parts: ${issues.map((issue) => issue.message).join(\"; \")}`);\n this.name = \"DatePartsError\";\n this.issues = issues;\n }\n}\n\n/**\n * An absolute instant was asked for, and nothing said which zone the local time\n * is in.\n *\n * This package never falls back to the host timezone, to UTC, or to any other\n * default, because a fallback turns an unknown into a plausible wrong answer.\n *\n * @example\n * ```ts\n * toInstant({ year: 2026, month: 3, day: 9, hour: 8, minute: 5 }); // throws MissingZoneError\n * ```\n */\nexport class MissingZoneError extends Error {\n /** @param message - What was asked for, and how to say which zone applies. */\n constructor(message: string) {\n super(message);\n this.name = \"MissingZoneError\";\n }\n}\n\n/**\n * The value does not carry enough precision for what was asked, and supplying\n * the missing components would mean inventing them.\n *\n * @example\n * ```ts\n * try {\n * toTemporal({ year: 1988 });\n * } catch (error) {\n * if (error instanceof PrecisionError) error.precision; // 'year'\n * }\n * ```\n */\nexport class PrecisionError extends Error {\n /** The precision the value actually has. */\n readonly precision: string;\n\n /**\n * @param message - What was asked for, and which components it needs.\n * @param precision - The precision the value actually has.\n */\n constructor(message: string, precision: string) {\n super(message);\n this.name = \"PrecisionError\";\n this.precision = precision;\n }\n}\n\n/**\n * A local time was placed in a named zone where it does not exist, or exists\n * twice.\n *\n * Both cases are resolved by REFUSING rather than by picking. A daylight-saving\n * transition is exactly where a silent choice shifts a clinical timeline by an\n * hour, so the caller is told and decides.\n *\n * @example\n * ```ts\n * const local = { year: 2026, month: 11, day: 1, hour: 1, minute: 30 };\n * toInstant(local, { timeZone: \"America/New_York\" });\n * // throws AmbiguousLocalTimeError: 2026-11-01T01:30:00 happens twice in America/New_York ...\n * ```\n */\nexport class AmbiguousLocalTimeError extends Error {\n /** @param message - The local time, the zone, and the candidate offsets where there are two. */\n constructor(message: string) {\n super(message);\n this.name = \"AmbiguousLocalTimeError\";\n }\n}\n","import type { ValidationIssue } from \"./types.js\";\n\n/**\n * Reading a `fraction` as whole nanoseconds, EXACTLY.\n *\n * One function answers \"how many nanoseconds is this?\" for the whole package, so\n * the validator and every converter cannot disagree about a value: a fraction the\n * validator accepts is one every converter can render, and a fraction any of them\n * refuses is refused by all of them with the same diagnostic.\n *\n * The count is read from the value's own decimal digits rather than computed as\n * `fraction * 1e9`. Binary floating point cannot hold most decimal fractions, so\n * that product misses whole nanoseconds by a hair and any tolerance wide enough\n * to forgive the hair is also wide enough to swallow a real difference. Digits\n * are exact and need no tolerance at all.\n */\n\n/** Temporal's resolution floor, and this package's: nine decimal places. */\nconst NANOSECOND_DIGITS = 9;\n\n/** `0`, or `0.` followed by the sub-second digits. */\nconst PLAIN_DECIMAL = /^0(?:\\.(\\d+))?$/;\n\n/** JavaScript prints a magnitude below `1e-6` in exponential form: `1e-9`, `1.5e-7`. */\nconst EXPONENTIAL_DECIMAL = /^(\\d)(?:\\.(\\d+))?e-(\\d+)$/;\n\n/**\n * The digits after the decimal point of the shortest decimal that round-trips to\n * `fraction`, or `undefined` if the value has no such form.\n *\n * The shortest round-tripping decimal is the value's canonical spelling: it is\n * what `0.123456789` was written as, and every longer decimal that lands on the\n * same double is the same double.\n */\nfunction fractionDigits(fraction: number): string | undefined {\n const text = String(fraction);\n\n const plain = PLAIN_DECIMAL.exec(text);\n if (plain !== null) return plain[1] ?? \"\";\n\n const exponential = EXPONENTIAL_DECIMAL.exec(text);\n if (exponential === null) return undefined;\n\n const lead = exponential[1] ?? \"\";\n const rest = exponential[2] ?? \"\";\n const exponent = Number(exponential[3]);\n return \"0\".repeat(exponent - 1) + lead + rest;\n}\n\n/**\n * The whole nanoseconds `fraction` denotes, or `undefined` when it carries detail\n * finer than one nanosecond.\n *\n * `fraction` must already be known finite and in `[0, 1)`; {@link readFraction}\n * is the entry point that establishes both. The result is therefore always in\n * `[0, 999999999]`, never a whole second.\n *\n * @internal Not exported from the package entry point.\n */\nexport function nanosecondsOf(fraction: number): number | undefined {\n const digits = fractionDigits(fraction);\n if (digits === undefined || digits.length > NANOSECOND_DIGITS) return undefined;\n return digits === \"\" ? 0 : Number(digits.padEnd(NANOSECOND_DIGITS, \"0\"));\n}\n\n/** A fraction read as whole nanoseconds, or the diagnostic that refuses it. */\nexport type FractionReading =\n | { readonly ok: true; readonly nanoseconds: number }\n | { readonly ok: false; readonly issue: ValidationIssue };\n\n/**\n * Check a `fraction` against the contract and read it as whole nanoseconds.\n *\n * Nothing is rounded. A value carrying detail below one nanosecond is REFUSED,\n * because rounding it away would silently change what the sender said, and this\n * package exists to not do that.\n *\n * @internal Not exported from the package entry point.\n */\nexport function readFraction(fraction: number): FractionReading {\n if (!Number.isFinite(fraction) || fraction < 0 || fraction >= 1) {\n return {\n ok: false,\n issue: {\n component: \"fraction\",\n code: \"out-of-range\",\n message: `fraction is a share of one second and must satisfy 0 <= fraction < 1; received the number ${String(fraction)}`,\n },\n };\n }\n\n const nanoseconds = nanosecondsOf(fraction);\n if (nanoseconds === undefined) {\n return {\n ok: false,\n issue: {\n component: \"fraction\",\n code: \"finer-than-nanosecond\",\n message: `fraction carries detail finer than one nanosecond, which this package will not round away; received the number ${String(fraction)}. One nanosecond is nine decimal places of a second, and it is the finest value Temporal can hold`,\n },\n };\n }\n\n return { ok: true, nanoseconds };\n}\n","import { DatePartsError } from \"./errors.js\";\nimport { readFraction } from \"./fraction.js\";\nimport { PRECISION_LADDER } from \"./types.js\";\nimport type {\n DateParts,\n InvalidPartsResult,\n PartName,\n Precision,\n ValidationIssue,\n ValidationResult,\n} from \"./types.js\";\n\nconst ALL_PARTS: readonly PartName[] = [...PRECISION_LADDER, \"offsetMinutes\"];\n\ntype IntegerPart = Exclude<PartName, \"fraction\">;\n\nconst INTEGER_RANGES: Readonly<Record<IntegerPart, readonly [number, number]>> = {\n year: [1000, 9999],\n month: [1, 12],\n day: [1, 31],\n hour: [0, 23],\n minute: [0, 59],\n second: [0, 59],\n offsetMinutes: [-1439, 1439],\n};\n\nconst RANGE_NOTES: Readonly<Partial<Record<IntegerPart, string>>> = {\n month: \" (spec-native, January is 1, not 0)\",\n offsetMinutes: \" (signed minutes from UTC)\",\n};\n\nconst MONTH_NAMES = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n] as const;\n\nconst MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const;\n\n/** Proleptic Gregorian, applied to every year in range with no exceptions. */\nfunction isLeapYear(year: number): boolean {\n return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n}\n\nfunction daysInMonth(year: number, month: number): number {\n if (month === 2) return isLeapYear(year) ? 29 : 28;\n return MONTH_LENGTHS[month - 1] ?? 0;\n}\n\nfunction monthName(month: number): string {\n return MONTH_NAMES[month - 1] ?? `month ${String(month)}`;\n}\n\n/** A short, non-throwing description of a value, for diagnostics. */\nfunction describe(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) return \"NaN\";\n if (!Number.isFinite(value)) return value > 0 ? \"Infinity\" : \"-Infinity\";\n return `the number ${String(value)}`;\n }\n if (typeof value === \"string\") return \"a string\";\n if (typeof value === \"boolean\") return `the boolean ${String(value)}`;\n if (typeof value === \"bigint\") return \"a bigint\";\n if (typeof value === \"symbol\") return \"a symbol\";\n if (typeof value === \"function\") return \"a function\";\n if (Array.isArray(value)) return \"an array\";\n return \"an object\";\n}\n\nfunction invalid(issues: readonly ValidationIssue[]): InvalidPartsResult {\n return { valid: false, issues };\n}\n\n/**\n * Read one named component off a candidate parts object.\n *\n * A component can be an accessor, and an accessor can throw. Reading it is\n * therefore guarded, so that a hostile or merely broken object is REPORTED like\n * any other fault rather than raising out of a function whose whole contract is\n * that it answers instead of throwing.\n */\nfunction readComponent(\n source: Record<string, unknown>,\n name: PartName,\n):\n | { readonly ok: true; readonly raw: unknown }\n | { readonly ok: false; readonly issue: ValidationIssue } {\n try {\n return { ok: true, raw: source[name] };\n } catch {\n return {\n ok: false,\n issue: {\n component: name,\n code: \"unreadable\",\n message: `${name} could not be read: the property threw when accessed, so this package cannot see what value it holds and will not guess one`,\n },\n };\n }\n}\n\n/**\n * Check a value against the date-parts contract.\n *\n * Every fault is reported, not only the first, and every diagnostic names the\n * component at fault together with the range or calendar rule it broke. Nothing\n * is coerced, clamped or inferred: a value that does not satisfy the contract\n * comes back invalid rather than corrected.\n *\n * This function never throws, for any argument at all. Hostile input (`null`, a\n * string, a JavaScript `Date`, `NaN`, `Infinity`, a non-integer component, a\n * component that throws when it is read) is reported, not raised.\n *\n * Components are looked up by name, so an object carrying them on its prototype\n * (a class instance with accessors, which is a shape a parser may well hand\n * over) is read the same way a plain object literal is.\n *\n * @example\n * ```ts\n * validateParts({ year: 1988, month: 5, day: 7 });\n * // { valid: true, parts: { year: 1988, month: 5, day: 7 }, precision: 'day' }\n *\n * const result = validateParts({ year: 1988, month: 13, day: 40 });\n * result.valid; // false\n * ```\n */\nexport function validateParts(value: unknown): ValidationResult {\n if (typeof value !== \"object\" || value === null) {\n return invalid([\n {\n component: \"value\",\n code: \"not-a-parts-object\",\n message: `a date-parts value must be an object carrying the components the contract names; received ${describe(value)}`,\n },\n ]);\n }\n\n if (value instanceof Date) {\n return invalid([\n {\n component: \"value\",\n code: \"not-a-parts-object\",\n message:\n \"a JavaScript Date is an absolute instant, not a date-parts value: it cannot express a year-only or month-only date, and it carries no offset of its own. Pass the parts the parser returned\",\n },\n ]);\n }\n\n if (Array.isArray(value)) {\n return invalid([\n {\n component: \"value\",\n code: \"not-a-parts-object\",\n message:\n \"a date-parts value must be an object with named components; received an array, whose positions this package will not guess at\",\n },\n ]);\n }\n\n const source = value as Record<string, unknown>;\n const issues: ValidationIssue[] = [];\n const present = new Set<PartName>();\n const values: Partial<Record<PartName, number>> = {};\n\n for (const name of ALL_PARTS) {\n const read = readComponent(source, name);\n if (!read.ok) {\n present.add(name);\n issues.push(read.issue);\n continue;\n }\n\n const raw = read.raw;\n // An explicit `undefined` reads as absent, the way an optional property does.\n if (raw === undefined) continue;\n present.add(name);\n\n if (typeof raw !== \"number\") {\n issues.push({\n component: name,\n code: \"not-a-number\",\n message: `${name} must be a number; received ${describe(raw)}`,\n });\n continue;\n }\n\n if (!Number.isFinite(raw)) {\n issues.push({\n component: name,\n code: \"not-finite\",\n message: `${name} must be a finite number; received ${describe(raw)}`,\n });\n continue;\n }\n\n if (name === \"fraction\") {\n const reading = readFraction(raw);\n if (reading.ok) values.fraction = raw;\n else issues.push(reading.issue);\n continue;\n }\n\n if (!Number.isInteger(raw)) {\n issues.push({\n component: name,\n code: \"not-an-integer\",\n message: `${name} must be a whole number; received the number ${String(raw)}`,\n });\n continue;\n }\n\n const [low, high] = INTEGER_RANGES[name];\n if (raw < low || raw > high) {\n issues.push(\n name === \"year\"\n ? {\n component: \"year\",\n code: \"year-not-four-digits\",\n message: `year must be a four-digit year in ${String(low)} to ${String(high)}; received the number ${String(raw)}. This package applies no century window and never interprets a two-digit year`,\n }\n : {\n component: name,\n code: \"out-of-range\",\n message: `${name} must be an integer in ${String(low)} to ${String(high)}${RANGE_NOTES[name] ?? \"\"}; received the number ${String(raw)}`,\n },\n );\n continue;\n }\n\n values[name] = raw;\n }\n\n checkContiguity(present, issues);\n checkOffsetHasTimeOfDay(present, issues);\n checkCalendarDate(values, issues);\n\n if (issues.length > 0) return invalid(issues);\n\n const parts: { -readonly [K in keyof DateParts]?: number } = {};\n for (const name of ALL_PARTS) {\n const held = values[name];\n if (held !== undefined) parts[name] = held;\n }\n\n return { valid: true, parts, precision: deepestPresent(present) as Precision };\n}\n\nfunction deepestPresent(present: ReadonlySet<PartName>): Precision | undefined {\n let deepest: Precision | undefined;\n for (const name of PRECISION_LADDER) {\n if (present.has(name)) deepest = name;\n }\n return deepest;\n}\n\nfunction checkContiguity(present: ReadonlySet<PartName>, issues: ValidationIssue[]): void {\n const deepest = deepestPresent(present);\n\n if (deepest === undefined) {\n issues.push({\n component: \"year\",\n code: \"missing-component\",\n message:\n \"year is missing: a date-parts value must carry at least a year, and this package never infers one\",\n });\n return;\n }\n\n const deepestIndex = PRECISION_LADDER.indexOf(deepest);\n for (let index = 0; index < deepestIndex; index += 1) {\n const name = PRECISION_LADDER[index];\n if (name === undefined || present.has(name)) continue;\n issues.push({\n component: name,\n code: \"missing-component\",\n message: `${name} is missing while ${deepest} is present: components run from year downward with no gaps, and this package never infers the one you left out`,\n });\n }\n}\n\nfunction checkOffsetHasTimeOfDay(present: ReadonlySet<PartName>, issues: ValidationIssue[]): void {\n if (!present.has(\"offsetMinutes\") || present.has(\"hour\")) return;\n issues.push({\n component: \"offsetMinutes\",\n code: \"offset-without-time-of-day\",\n message:\n \"offsetMinutes is present but the value states no time of day: an offset qualifies a clock time, it cannot be rendered without one, and dropping it silently is not an option this package takes\",\n });\n}\n\nfunction checkCalendarDate(\n values: Partial<Record<PartName, number>>,\n issues: ValidationIssue[],\n): void {\n const { year, month, day } = values;\n if (year === undefined || month === undefined || day === undefined) return;\n\n const limit = daysInMonth(year, month);\n if (day <= limit) return;\n\n const leapNote =\n month === 2\n ? `, because ${String(year)} is ${isLeapYear(year) ? \"a\" : \"not a\"} leap year in the proleptic Gregorian calendar`\n : \"\";\n issues.push({\n component: \"day\",\n code: \"no-such-calendar-date\",\n message: `day ${String(day)} does not exist: ${monthName(month)} ${String(year)} has ${String(limit)} days${leapNote}`,\n });\n}\n\n/**\n * A type guard over {@link validateParts}, for callers that want a boolean.\n *\n * @example\n * ```ts\n * isValidParts({ year: 2024, month: 2, day: 29 }); // true\n * isValidParts({ year: 2025, month: 2, day: 29 }); // false\n * ```\n */\nexport function isValidParts(value: unknown): value is DateParts {\n return validateParts(value).valid;\n}\n\n/**\n * Validate and return the recognised components, or throw {@link DatePartsError}\n * carrying the same diagnostics {@link validateParts} would have reported.\n *\n * @example\n * ```ts\n * assertValidParts({ year: 1988, month: 5 }); // { year: 1988, month: 5 }\n * assertValidParts({ year: 2025, month: 2, day: 29 }); // throws DatePartsError\n * ```\n */\nexport function assertValidParts(value: unknown): DateParts {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return result.parts;\n}\n\n/**\n * The precision of a value: its least significant present component.\n *\n * Throws {@link DatePartsError} for a value that does not satisfy the contract,\n * because an invalid value has no precision to report.\n *\n * @example\n * ```ts\n * precisionOf({ year: 1988 }); // 'year'\n * precisionOf({ year: 1988, month: 5, day: 7, hour: 13 }); // 'hour'\n * ```\n */\nexport function precisionOf(value: unknown): Precision {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return result.precision;\n}\n","import { Temporal } from \"temporal-polyfill\";\n\nimport {\n AmbiguousLocalTimeError,\n DatePartsError,\n MissingZoneError,\n PrecisionError,\n} from \"./errors.js\";\nimport { readFraction } from \"./fraction.js\";\nimport { PRECISION_LADDER } from \"./types.js\";\nimport type { DateParts, Precision, ZoneOptions } from \"./types.js\";\nimport { validateParts } from \"./validate.js\";\n\n/** The narrowest Temporal type that carries everything a value states, and nothing more. */\nexport type TemporalValue =\n Temporal.PlainYearMonth | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime;\n\ninterface Checked {\n readonly parts: DateParts;\n readonly precision: Precision;\n}\n\nfunction check(value: unknown): Checked {\n const result = validateParts(value);\n if (!result.valid) throw new DatePartsError(result.issues);\n return { parts: result.parts, precision: result.precision };\n}\n\nfunction atLeast(precision: Precision, floor: Precision): boolean {\n return PRECISION_LADDER.indexOf(precision) >= PRECISION_LADDER.indexOf(floor);\n}\n\nfunction pad(value: number, width: number): string {\n return String(value).padStart(width, \"0\");\n}\n\n/**\n * Signed minutes rendered as `+HH:MM` or `-HH:MM`.\n *\n * A present offset of zero renders `+00:00` rather than `Z`, so the ISO string\n * says the same thing the parts do: an offset was stated, and it was zero. An\n * ABSENT offset renders nothing at all, because unknown is not UTC.\n */\nfunction formatOffset(offsetMinutes: number): string {\n const sign = offsetMinutes < 0 ? \"-\" : \"+\";\n const absolute = Math.abs(offsetMinutes);\n return `${sign}${pad(Math.trunc(absolute / 60), 2)}:${pad(absolute % 60, 2)}`;\n}\n\n/**\n * The whole nanoseconds a fraction denotes.\n *\n * Every converter reads a fraction through the SAME function the validator uses,\n * so a value the validator has just certified can never reach a converter that\n * disagrees with it. A fraction the reader refuses is refused here with the\n * validator's own diagnostic rather than by leaking whatever Temporal makes of\n * a nonsense field.\n *\n * @throws {DatePartsError} if the fraction is not a whole number of nanoseconds.\n */\nfunction nanosecondsOf(fraction: number): number {\n const reading = readFraction(fraction);\n if (!reading.ok) throw new DatePartsError([reading.issue]);\n return reading.nanoseconds;\n}\n\n/** Sub-second digits, trimmed of trailing zeros but never to nothing. */\nfunction formatFraction(fraction: number): string {\n const digits = pad(nanosecondsOf(fraction), 9).replace(/0+$/, \"\");\n return `.${digits === \"\" ? \"0\" : digits}`;\n}\n\nfunction subSecondFields(fraction: number | undefined): {\n millisecond?: number;\n microsecond?: number;\n nanosecond?: number;\n} {\n if (fraction === undefined) return {};\n const nanoseconds = nanosecondsOf(fraction);\n return {\n millisecond: Math.floor(nanoseconds / 1e6),\n microsecond: Math.floor((nanoseconds % 1e6) / 1e3),\n nanosecond: nanoseconds % 1e3,\n };\n}\n\n/**\n * Render a value as an ISO 8601 string AT ITS OWN PRECISION.\n *\n * A year-only value renders `1988`; a month-only value renders `1988-05`. No\n * component the value did not state is ever added, so nothing here turns a year\n * of birth into the first of January.\n *\n * The result is a pure function of the parts. It does not read the host clock or\n * the host time zone, so it is identical under every `TZ`.\n *\n * @example\n * ```ts\n * toISO({ year: 1988 }); // '1988'\n * toISO({ year: 2026, month: 3, day: 9, hour: 8 }); // '2026-03-09T08'\n * toISO({ year: 2026, month: 3, day: 9, hour: 8, minute: 5, offsetMinutes: -300 });\n * // '2026-03-09T08:05-05:00'\n * ```\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n */\nexport function toISO(value: unknown): string {\n const { parts, precision } = check(value);\n const suffix = parts.offsetMinutes === undefined ? \"\" : formatOffset(parts.offsetMinutes);\n\n let out = pad(parts.year ?? 0, 4);\n if (precision === \"year\") return out;\n\n out += `-${pad(parts.month ?? 0, 2)}`;\n if (precision === \"month\") return out;\n\n out += `-${pad(parts.day ?? 0, 2)}`;\n if (precision === \"day\") return out;\n\n out += `T${pad(parts.hour ?? 0, 2)}`;\n if (precision === \"hour\") return out + suffix;\n\n out += `:${pad(parts.minute ?? 0, 2)}`;\n if (precision === \"minute\") return out + suffix;\n\n out += `:${pad(parts.second ?? 0, 2)}`;\n if (precision === \"second\") return out + suffix;\n\n out += formatFraction(parts.fraction ?? 0);\n return out + suffix;\n}\n\n/**\n * Convert to the narrowest Temporal type that carries everything the value\n * states, and nothing it does not.\n *\n * | precision of the value | result |\n * |---|---|\n * | `month` | `Temporal.PlainYearMonth` |\n * | `day` | `Temporal.PlainDate`, with no time of day and no zone |\n * | `hour` and finer, no offset | `Temporal.PlainDateTime`, with no zone |\n * | `hour` and finer, with `offsetMinutes` | `Temporal.ZonedDateTime` at that exact offset |\n *\n * A year-only value has no Temporal counterpart and raises\n * {@link PrecisionError} rather than acquiring a month. Use {@link toISO} for\n * those, and {@link precisionOf} to branch.\n *\n * Components below the value's own precision are zero in the Temporal object,\n * because Temporal has no partial type below `PlainYearMonth`. The value's real\n * precision is still available from {@link precisionOf}; it is not lost, it just\n * cannot be carried by the returned object.\n *\n * @example\n * ```ts\n * toTemporal({ year: 2026, month: 3 }).toString(); // '2026-03'\n * toTemporal({ year: 2026, month: 3, day: 9 }).toString(); // '2026-03-09'\n * toTemporal({ year: 2026, month: 3, day: 9, hour: 8, minute: 5 }).toString();\n * // '2026-03-09T08:05:00'\n * ```\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n * @throws {PrecisionError} for a year-only value.\n */\nexport function toTemporal(value: unknown): TemporalValue {\n const { parts, precision } = check(value);\n\n if (precision === \"year\") {\n throw new PrecisionError(\n \"Temporal has no year-only type, and this package will not invent a month to reach one. Use toISO() to render a year-precision value, or precisionOf() to branch before converting\",\n precision,\n );\n }\n\n const year = parts.year ?? 0;\n const month = parts.month ?? 0;\n\n if (precision === \"month\") {\n return Temporal.PlainYearMonth.from({ year, month }, { overflow: \"reject\" });\n }\n\n const day = parts.day ?? 0;\n if (precision === \"day\") {\n return Temporal.PlainDate.from({ year, month, day }, { overflow: \"reject\" });\n }\n\n const fields = {\n year,\n month,\n day,\n hour: parts.hour ?? 0,\n minute: parts.minute ?? 0,\n second: parts.second ?? 0,\n ...subSecondFields(parts.fraction),\n };\n\n if (parts.offsetMinutes !== undefined) {\n return Temporal.ZonedDateTime.from(\n { ...fields, timeZone: formatOffset(parts.offsetMinutes) },\n { overflow: \"reject\" },\n );\n }\n\n return Temporal.PlainDateTime.from(fields, { overflow: \"reject\" });\n}\n\nfunction assertKnownTimeZone(timeZone: string): void {\n try {\n // A fixed epoch, so this probe never reads the host clock or the host zone.\n Temporal.Instant.fromEpochMilliseconds(0).toZonedDateTimeISO(timeZone);\n } catch {\n throw new RangeError(\n `unknown time zone ${JSON.stringify(timeZone)}: supply an IANA identifier such as \"America/New_York\", or an offset such as \"+05:30\"`,\n );\n }\n}\n\nfunction assertUsableOffset(offsetMinutes: number): void {\n if (!Number.isInteger(offsetMinutes) || offsetMinutes < -1439 || offsetMinutes > 1439) {\n throw new RangeError(\n `offsetMinutes must be an integer in -1439 to 1439 (signed minutes from UTC); received ${String(offsetMinutes)}`,\n );\n }\n}\n\n/**\n * Decide which zone places this value on the timeline, or refuse.\n *\n * The value's OWN `offsetMinutes` always wins. That is what the sender said, and\n * re-zoning it on a caller's say-so would move the instant. `options` therefore\n * apply only to a value whose offset is unknown, which is exactly the case they\n * exist for.\n */\nfunction resolveTimeZone(parts: DateParts, options: ZoneOptions | undefined): string {\n if (parts.offsetMinutes !== undefined) return formatOffset(parts.offsetMinutes);\n\n if (options?.timeZone !== undefined && options.offsetMinutes !== undefined) {\n throw new TypeError(\n \"supply either timeZone or offsetMinutes, not both: two answers to one question is a guess waiting to happen\",\n );\n }\n\n if (options?.offsetMinutes !== undefined) {\n assertUsableOffset(options.offsetMinutes);\n return formatOffset(options.offsetMinutes);\n }\n\n if (options?.timeZone !== undefined) {\n assertKnownTimeZone(options.timeZone);\n return options.timeZone;\n }\n\n throw new MissingZoneError(\n \"this value carries no offsetMinutes, and no zone was supplied: an absolute instant needs a zone, and this package supplies none. Pass { timeZone } or { offsetMinutes }. It will not fall back to the host time zone, to UTC, or to any other default, because a fallback turns an unknown offset into a plausible wrong instant\",\n );\n}\n\n/**\n * Place a value on the timeline in a stated zone.\n *\n * Requires a time of day. A day-precision value is a calendar date, and turning\n * one into a zoned instant means choosing midnight, which is a component the\n * value does not carry. Use {@link toTemporal} for those.\n *\n * Requires a zone. If the value carries `offsetMinutes` that offset is used and\n * PRESERVED EXACTLY, never normalised to UTC or to the host zone, and `options`\n * are not consulted. Otherwise `options.timeZone` or `options.offsetMinutes`\n * must say which zone the local time is in.\n *\n * A local time that a named zone skips or repeats across a daylight-saving\n * transition raises {@link AmbiguousLocalTimeError} rather than resolving to\n * one of the two candidates, because a silent pick shifts the value by an hour.\n *\n * @throws {DatePartsError} if the value does not satisfy the contract.\n * @throws {PrecisionError} if the value states no time of day.\n * @throws {MissingZoneError} if nothing says which zone applies.\n * @throws {AmbiguousLocalTimeError} at a daylight-saving transition.\n * @example\n * ```ts\n * const local = { year: 2026, month: 3, day: 9, hour: 8, minute: 5 };\n * toZonedDateTime(local, { timeZone: \"America/New_York\" }).toString();\n * // '2026-03-09T08:05:00-04:00[America/New_York]'\n * ```\n */\nexport function toZonedDateTime(value: unknown, options?: ZoneOptions): Temporal.ZonedDateTime {\n const { parts, precision } = check(value);\n\n if (!atLeast(precision, \"hour\")) {\n // A value below hour precision that also states no zone is missing BOTH, and\n // hearing about them one at a time is how a caller fixes one and comes back.\n // The precision is what is reported, because supplying a zone would not make\n // this value convertible, but the message says the zone is absent too, and\n // says as firmly as the zone refusal does that nothing will be defaulted.\n const alsoNoZone =\n parts.offsetMinutes === undefined &&\n options?.timeZone === undefined &&\n options?.offsetMinutes === undefined;\n\n throw new PrecisionError(\n `an absolute instant needs a time of day, and this value has ${precision} precision. Supplying one would mean inventing components the value does not carry. Use toTemporal() for the calendar value${\n alsoNoZone\n ? \". The zone is missing too: this value carries no offsetMinutes and no { timeZone } or { offsetMinutes } was supplied, and neither the host time zone nor UTC will be used in their place\"\n : \"\"\n }`,\n precision,\n );\n }\n\n const timeZone = resolveTimeZone(parts, options);\n const local = Temporal.PlainDateTime.from(\n {\n year: parts.year ?? 0,\n month: parts.month ?? 0,\n day: parts.day ?? 0,\n hour: parts.hour ?? 0,\n minute: parts.minute ?? 0,\n second: parts.second ?? 0,\n ...subSecondFields(parts.fraction),\n },\n { overflow: \"reject\" },\n );\n\n // Ambiguity is established by ASKING, not by catching. The two disambiguation\n // rules agree on every local time a zone maps exactly once, and disagree on\n // exactly the times a transition skips or repeats. Reading the answer off a\n // thrown message would instead relabel any other failure as a transition, and\n // a fixed offset zone, which has no transitions at all, would be told it did.\n const earliest = local.toZonedDateTime(timeZone, { disambiguation: \"earlier\" });\n const latest = local.toZonedDateTime(timeZone, { disambiguation: \"later\" });\n\n if (!earliest.equals(latest)) {\n // A repeated local time still reads back as itself, at two offsets. A skipped\n // one cannot: both rules move it off the gap, so the wall clock shifts.\n const skipped = !earliest.toPlainDateTime().equals(local);\n throw new AmbiguousLocalTimeError(\n skipped\n ? `${local.toString()} does not exist in ${timeZone}: a daylight-saving transition skips it, so there is no instant to return. Say which side of the transition you mean by passing that offset as { offsetMinutes }`\n : `${local.toString()} happens twice in ${timeZone}: a daylight-saving transition repeats it, at ${earliest.offset} and again at ${latest.offset}, so there is no single instant to return. Choose the one you mean and pass it as { offsetMinutes }`,\n );\n }\n\n return earliest;\n}\n\n/**\n * The absolute instant a value denotes, in a stated zone.\n *\n * Same requirements as {@link toZonedDateTime}: a time of day, and a zone that\n * something states. Nothing is defaulted.\n *\n * @example\n * ```ts\n * const local = { year: 2026, month: 3, day: 9, hour: 8, minute: 5 };\n * toInstant(local, { timeZone: \"America/New_York\" }).toString(); // '2026-03-09T12:05:00Z'\n * toInstant({ ...local, offsetMinutes: -300 }).toString(); // '2026-03-09T13:05:00Z'\n * ```\n */\nexport function toInstant(value: unknown, options?: ZoneOptions): Temporal.Instant {\n return toZonedDateTime(value, options).toInstant();\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosyte/dates",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Date and time utilities for the @cosyte/* healthcare parsers: precision-preserving conversion and validation that never guesses a timezone.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Cosyte",
|
|
@@ -54,32 +54,44 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@arethetypeswrong/cli": "0.18.4",
|
|
56
56
|
"@changesets/cli": "2.31.0",
|
|
57
|
-
"@cosyte/
|
|
58
|
-
"@
|
|
57
|
+
"@cosyte/eslint-config": "^0.1.2",
|
|
58
|
+
"@cosyte/prettier-config": "^0.1.2",
|
|
59
|
+
"@cosyte/script-utils": "^0.1.4",
|
|
60
|
+
"@cosyte/tsconfig": "^0.1.2",
|
|
61
|
+
"@cosyte/tsup-config": "^0.1.2",
|
|
62
|
+
"@cosyte/vitest-config": "^0.1.2",
|
|
59
63
|
"@types/node": "22.20.1",
|
|
64
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
60
65
|
"eslint": "10.9.1",
|
|
61
66
|
"prettier": "3.9.6",
|
|
62
67
|
"tsup": "8.5.1",
|
|
63
68
|
"tsx": "4.21.0",
|
|
64
69
|
"typescript": "5.9.3",
|
|
65
|
-
"
|
|
70
|
+
"vite": "8.2.2",
|
|
66
71
|
"vitest": "4.1.11"
|
|
67
72
|
},
|
|
73
|
+
"prettier": "@cosyte/prettier-config",
|
|
68
74
|
"scripts": {
|
|
69
75
|
"build": "tsup",
|
|
70
76
|
"changeset": "changeset",
|
|
71
77
|
"typecheck": "tsc --noEmit",
|
|
78
|
+
"phi-scan": "cosyte-phi-scan",
|
|
79
|
+
"check:readme": "cosyte-readme",
|
|
80
|
+
"check:claude-card": "cosyte-claude-card",
|
|
81
|
+
"check:agent-notes": "cosyte-agent-notes",
|
|
82
|
+
"check:catalog": "cosyte-catalog",
|
|
72
83
|
"lint": "eslint . --max-warnings=0",
|
|
73
84
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"scripts/**/*.{ts,mjs}\" \"examples/**/*.{ts,md}\" \"docs-content/**/*.{md,json}\" \"*.{ts,json,md,yml}\"",
|
|
74
85
|
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"scripts/**/*.{ts,mjs}\" \"examples/**/*.{ts,md}\" \"docs-content/**/*.{md,json}\" \"*.{ts,json,md,yml}\"",
|
|
75
86
|
"test": "vitest run",
|
|
87
|
+
"test:coverage": "vitest run --coverage",
|
|
76
88
|
"test:tz": "TZ=UTC vitest run && TZ=Pacific/Kiritimati vitest run",
|
|
77
89
|
"examples": "tsx scripts/run-examples.ts",
|
|
78
90
|
"clean": "rm -rf dist coverage .tmp dist-artifacts",
|
|
79
|
-
"attw": "
|
|
91
|
+
"attw": "cosyte-attw",
|
|
80
92
|
"pack:docs": "bash scripts/build-docs-artifacts.sh",
|
|
81
|
-
"check:no-emdash": "
|
|
82
|
-
"check:no-internal-refs": "
|
|
93
|
+
"check:no-emdash": "cosyte-no-emdash",
|
|
94
|
+
"check:no-internal-refs": "cosyte-internal-refs",
|
|
83
95
|
"version": "changeset version && prettier --write package.json CHANGELOG.md",
|
|
84
96
|
"release": "changeset publish"
|
|
85
97
|
}
|