@cosyte/dates
Classes
AmbiguousLocalTimeError
A local time was placed in a named zone where it does not exist, or exists twice.
Both cases are resolved by REFUSING rather than by picking. A daylight-saving transition is exactly where a silent choice shifts a clinical timeline by an hour, so the caller is told and decides.
Extends
Error
Constructors
Constructor
new AmbiguousLocalTimeError(
message):AmbiguousLocalTimeError
Parameters
message
string
Returns
Overrides
Error.constructor
DatePartsError
A conversion was asked for a value that validation reports invalid.
issues is the SAME array validateParts() returns for that value, so a
caller that catches this never has to validate again to learn what was wrong,
and a conversion never returns a coerced or clamped stand-in.
Extends
Error
Constructors
Constructor
new DatePartsError(
issues):DatePartsError
Parameters
issues
readonly ValidationIssue[]
Returns
Overrides
Error.constructor
Properties
issues
readonlyissues: readonlyValidationIssue[]
MissingZoneError
An absolute instant was asked for, and nothing said which zone the local time is in.
This package never falls back to the host timezone, to UTC, or to any other default, because a fallback turns an unknown into a plausible wrong answer.
Extends
Error
Constructors
Constructor
new MissingZoneError(
message):MissingZoneError
Parameters
message
string
Returns
Overrides
Error.constructor
PrecisionError
The value does not carry enough precision for what was asked, and supplying the missing components would mean inventing them.
Extends
Error
Constructors
Constructor
new PrecisionError(
message,precision):PrecisionError
Parameters
message
string
precision
string
Returns
Overrides
Error.constructor
Properties
precision
readonlyprecision:string
The precision the value actually has.
Interfaces
DateParts
The date-parts value every @cosyte/* parser hands back, and the only shape
this package accepts.
Two rules make it a contract rather than a bag of numbers.
- CONTIGUITY. Components are present from the most significant downward with
no gaps.
{ year, month }is legal;{ year, day }is not. - PRECISION IS WHAT IS PRESENT. The least significant present component IS
the precision of the value. Nothing here adds a component that was not
supplied, and an absent
offsetMinutesmeans the offset is UNKNOWN, which is not the same as UTC and not the same as the host zone.
month is SPEC-NATIVE 1-12, not JavaScript's 0-11.
Properties
day?
readonlyoptionalday?:number
1-31, and a day that exists in the given month of the given year.
fraction?
readonlyoptionalfraction?:number
Sub-second precision as a fraction of one second: 0 <= fraction < 1.
hour?
readonlyoptionalhour?:number
0-23.
minute?
readonlyoptionalminute?:number
0-59.
month?
readonlyoptionalmonth?:number
1-12, spec-native. January is 1.
offsetMinutes?
readonlyoptionaloffsetMinutes?:number
Signed minutes from UTC, -1439 to 1439. ABSENT means the offset is unknown. It never means zero, and it is never filled in from the host zone.
second?
readonlyoptionalsecond?:number
0-59. Leap seconds are not representable.
year?
readonlyoptionalyear?:number
Four digits, 1000-9999. Required. Two-digit years are never interpreted.
InvalidPartsResult
An invalid value, with every fault found rather than only the first.
Properties
issues
readonlyissues: readonlyValidationIssue[]
valid
readonlyvalid:false
ValidationIssue
One thing wrong with one component, named.
Properties
code
readonlycode:IssueCode
component
readonlycomponent:PartName|"value"
The component at fault, or 'value' when the argument itself is wrong.
message
readonlymessage:string
Human-readable, names the component and the range or calendar rule broken.
ValidPartsResult
A validated value, carrying only the recognised components that were present.
Properties
parts
readonlyparts:DateParts
precision
readonlyprecision:"year"|"month"|"day"|"hour"|"minute"|"second"|"fraction"
valid
readonlyvalid:true
ZoneOptions
How to place a value that carries no offset of its own onto the timeline.
A value that DOES carry offsetMinutes is never re-zoned: its own offset is
what the sender said, and overriding it here would move the instant.
Properties
offsetMinutes?
readonlyoptionaloffsetMinutes?:number
A fixed offset in signed minutes from UTC, -1439 to 1439.
timeZone?
readonlyoptionaltimeZone?:string
An IANA time zone identifier, for example 'America/New_York'.
Type Aliases
IssueCode
IssueCode =
"not-a-parts-object"|"missing-component"|"unreadable"|"not-a-number"|"not-finite"|"not-an-integer"|"out-of-range"|"year-not-four-digits"|"finer-than-nanosecond"|"offset-without-time-of-day"|"no-such-calendar-date"
What a diagnostic says went wrong.
PartName
PartName =
Precision|"offsetMinutes"
Every component name the contract defines.
Precision
Precision = typeof
PRECISION_LADDER[number]
The least significant component present in a value IS its precision.
TemporalValue
TemporalValue =
Temporal.PlainYearMonth|Temporal.PlainDate|Temporal.PlainDateTime|Temporal.ZonedDateTime
The narrowest Temporal type that carries everything a value states, and nothing more.
ValidationResult
ValidationResult =
ValidPartsResult|InvalidPartsResult
Variables
PRECISION_LADDER
constPRECISION_LADDER: readonly ["year","month","day","hour","minute","second","fraction"]
The precision ladder, most significant first.
Temporal
Temporal:
any
The Temporal implementation this package converts into, re-exported so a
consumer works with the same objects rather than installing a second copy.
Two Temporal implementations do not share instanceof.
Functions
assertValidParts()
assertValidParts(
value):DateParts
Validate and return the recognised components, or throw DatePartsError carrying the same diagnostics validateParts would have reported.
Parameters
value
unknown
Returns
isValidParts()
isValidParts(
value):value is DateParts
A type guard over validateParts, for callers that want a boolean.
Parameters
value
unknown
Returns
value is DateParts
precisionOf()
precisionOf(
value):"year"|"month"|"day"|"hour"|"minute"|"second"|"fraction"
The precision of a value: its least significant present component.
Throws DatePartsError for a value that does not satisfy the contract, because an invalid value has no precision to report.
Parameters
value
unknown
Returns
"year" | "month" | "day" | "hour" | "minute" | "second" | "fraction"
toInstant()
toInstant(
value,options?):Instant
The absolute instant a value denotes, in a stated zone.
Same requirements as toZonedDateTime: a time of day, and a zone that something states. Nothing is defaulted.
Parameters
value
unknown
options?
Returns
Instant
toISO()
toISO(
value):string
Render a value as an ISO 8601 string AT ITS OWN PRECISION.
A year-only value renders 1988; a month-only value renders 1988-05. No
component the value did not state is ever added, so nothing here turns a year
of birth into the first of January.
The result is a pure function of the parts. It does not read the host clock or
the host time zone, so it is identical under every TZ.
Parameters
value
unknown
Returns
string
Throws
if the value does not satisfy the contract.
toTemporal()
toTemporal(
value):any
Convert to the narrowest Temporal type that carries everything the value states, and nothing it does not.
| precision of the value | result |
|---|---|
month | Temporal.PlainYearMonth |
day | Temporal.PlainDate, with no time of day and no zone |
hour and finer, no offset | Temporal.PlainDateTime, with no zone |
hour and finer, with offsetMinutes | Temporal.ZonedDateTime at that exact offset |
A year-only value has no Temporal counterpart and raises PrecisionError rather than acquiring a month. Use toISO for those, and precisionOf to branch.
Components below the value's own precision are zero in the Temporal object,
because Temporal has no partial type below PlainYearMonth. The value's real
precision is still available from precisionOf; it is not lost, it just
cannot be carried by the returned object.
Parameters
value
unknown
Returns
any
Throws
if the value does not satisfy the contract.
Throws
for a year-only value.
toZonedDateTime()
toZonedDateTime(
value,options?):ZonedDateTime
Place a value on the timeline in a stated zone.
Requires a time of day. A day-precision value is a calendar date, and turning one into a zoned instant means choosing midnight, which is a component the value does not carry. Use toTemporal for those.
Requires a zone. If the value carries offsetMinutes that offset is used and
PRESERVED EXACTLY, never normalised to UTC or to the host zone, and options
are not consulted. Otherwise options.timeZone or options.offsetMinutes
must say which zone the local time is in.
A local time that a named zone skips or repeats across a daylight-saving transition raises AmbiguousLocalTimeError rather than resolving to one of the two candidates, because a silent pick shifts the value by an hour.
Parameters
value
unknown
options?
Returns
ZonedDateTime
Throws
if the value does not satisfy the contract.
Throws
if the value states no time of day.
Throws
if nothing says which zone applies.
Throws
at a daylight-saving transition.
validateParts()
validateParts(
value):ValidationResult
Check a value against the date-parts contract.
Every fault is reported, not only the first, and every diagnostic names the component at fault together with the range or calendar rule it broke. Nothing is coerced, clamped or inferred: a value that does not satisfy the contract comes back invalid rather than corrected.
This function never throws, for any argument at all. Hostile input (null, a
string, a JavaScript Date, NaN, Infinity, a non-integer component, a
component that throws when it is read) is reported, not raised.
Components are looked up by name, so an object carrying them on its prototype (a class instance with accessors, which is a shape a parser may well hand over) is read the same way a plain object literal is.
Parameters
value
unknown