Abstract Operations

ISODateToEpochDays ( _year_: an integer, _month_: an integer, _date_: an integer, ): an integer

description
It calculates a number of days.
1. Let _resolvedYear_ be _year_ + floor(_month_ / 12). 1. Let _resolvedMonth_ be _month_ modulo 12. 1. Find a time _t_ such that EpochTimeToEpochYear(_t_) = _resolvedYear_, EpochTimeToMonthInYear(_t_) = _resolvedMonth_, and EpochTimeToDate(_t_) = 1. 1. Return EpochTimeToDayNumber(_t_) + _date_ - 1. This operation corresponds to ECMA-262 operation MakeDay(_year_, _month_, _date_). It calculates the result in mathematical values instead of Number values. These two operations would be unified when https://github.com/tc39/ecma262/issues/1087 is fixed.

EpochDaysToEpochMs ( _day_: an integer, _time_: an integer, ): an integer

description
It calculates a number of milliseconds.
1. Return _day_ × ℝ(msPerDay) + _time_. This operation corresponds to ECMA-262 operation MakeDate(_date_, _time_). It calculates the result in mathematical values instead of Number values. These two operations would be unified when https://github.com/tc39/ecma262/issues/1087 is fixed.

Date Equations

A given time _t_ belongs to day number

EpochTimeToDayNumber(_t_) = floor(_t_ / ℝ(msPerDay))

Number of days in year are given by:

MathematicalDaysInYear(_y_) = 365 if ((_y_) modulo 4) ≠ 0 = 366 if ((_y_) modulo 4) = 0 and ((_y_) modulo 100) ≠ 0 = 365 if ((_y_) modulo 100) = 0 and ((_y_) modulo 400) ≠ 0 = 366 if ((_y_) modulo 400) = 0

The day number of the first day of year _y_ is given by:

EpochDayNumberForYear(_y_) = 365 × (_y_ - 1970) + floor((_y_ - 1969) / 4) - floor((_y_ - 1901) / 100) + floor((_y_ - 1601) / 400)

The time of the start of a year is:

EpochTimeForYear(_y_) = ℝ(msPerDay) × EpochDayNumberForYear(_y_)

Epoch year from time _t_ is given by:

EpochTimeToEpochYear(_t_) = the largest integral Number _y_ (closest to +∞) such that EpochTimeForYear(_y_) ≤ _t_

The following function returns 1 for a time within leap year otherwise it returns 0:

MathematicalInLeapYear(_t_) = 0 if MathematicalDaysInYear(EpochTimeToEpochYear(_t_)) = 365 = 1 if MathematicalDaysInYear(EpochTimeToEpochYear(_t_)) = 366

The month number for a time _t_ is given by:

EpochTimeToMonthInYear(_t_) = 0 if 0 ≤ EpochTimeToDayInYear(_t_) < 31 = 1 if 31 ≤ EpochTimeToDayInYear(_t_) < 59 + MathematicalInLeapYear(_t_) = 2 if 59 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 90 + MathematicalInLeapYear(_t_) = 3 if 90 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 120 + MathematicalInLeapYear(_t_) = 4 if 120 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 151 + MathematicalInLeapYear(_t_) = 5 if 151 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 181 + MathematicalInLeapYear(_t_) = 6 if 181 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 212 + MathematicalInLeapYear(_t_) = 7 if 212 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 243 + MathematicalInLeapYear(_t_) = 8 if 243 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 273 + MathematicalInLeapYear(_t_) = 9 if 273 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 304 + MathematicalInLeapYear(_t_) = 10 if 304 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 334 + MathematicalInLeapYear(_t_) = 11 if 334 + MathematicalInLeapYear(_t_) ≤ EpochTimeToDayInYear(_t_) < 365 + MathematicalInLeapYear(_t_)

where

EpochTimeToDayInYear(_t_) = EpochTimeToDayNumber(_t_) - EpochDayNumberForYear(EpochTimeToEpochYear(_t_))

A month value of 0 specifies January; 1 specifies February; 2 specifies March; 3 specifies April; 4 specifies May; 5 specifies June; 6 specifies July; 7 specifies August; 8 specifies September; 9 specifies October; 10 specifies November; and 11 specifies December. Note that EpochTimeToMonthInYear(0) = 0, corresponding to Thursday, 1 January 1970.

The date number for a time _t_ is given by:

EpochTimeToDate(_t_) = EpochTimeToDayInYear(_t_) + 1 if EpochTimeToMonthInYear(_t_) = 0 = EpochTimeToDayInYear(_t_) - 30 if EpochTimeToMonthInYear(_t_) = 1 = EpochTimeToDayInYear(_t_) - 58 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 2 = EpochTimeToDayInYear(_t_) - 89 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 3 = EpochTimeToDayInYear(_t_) - 119 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 4 = EpochTimeToDayInYear(_t_) - 150 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 5 = EpochTimeToDayInYear(_t_) - 180 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 6 = EpochTimeToDayInYear(_t_) - 211 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 7 = EpochTimeToDayInYear(_t_) - 242 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 8 = EpochTimeToDayInYear(_t_) - 272 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 9 = EpochTimeToDayInYear(_t_) - 303 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 10 = EpochTimeToDayInYear(_t_) - 333 - MathematicalInLeapYear(_t_) if EpochTimeToMonthInYear(_t_) = 11

The weekday for a particular time _t_ is defined as:

EpochTimeToWeekDay(_t_) =(EpochTimeToDayNumber(_t_) + 4) modulo 7

A weekday value of 0 specifies Sunday; 1 specifies Monday; 2 specifies Tuesday; 3 specifies Wednesday; 4 specifies Thursday; 5 specifies Friday; and 6 specifies Saturday. Note that EpochTimeToWeekDay(0) = 4, corresponding to Thursday, 1 January 1970.

These equations correspond to ECMA-262 equations defined in Days in Year, Month from Time, Date from Time, Week Day respectively. These calculate the result in mathematical values instead of Number values. These equations would be unified when https://github.com/tc39/ecma262/issues/1087 is fixed. Note that the operation EpochTimeToMonthInYear(_t_) uses 0-based months unlike rest of Temporal since it's intended to be unified with MonthFromTime(_t_) when the above mentioned issue is fixed.

CheckISODaysRange ( _isoDate_: an ISO Date Record, ): either a normal completion containing ~unused~ or a throw completion

description
It checks that the given date is within the range of 108 days from the epoch.
1. If abs(ISODateToEpochDays(_isoDate_.[[Year]], _isoDate_.[[Month]] - 1, _isoDate_.[[Day]])) > 108, then 1. Throw a *RangeError* exception. 1. Return ~unused~. This operation is solely present to ensure that GetUTCEpochNanoseconds is not called with numbers that are too large. It is distinct from ISODateWithinLimits, which uses GetUTCEpochNanoseconds. This operation can be removed with no observable effect when https://github.com/tc39/ecma262/issues/1087 is fixed.

Units

Time is reckoned using multiple units. These units are listed in .

A Temporal unit is a value listed in the "Value" column of . A calendar unit is a Temporal unit for which IsCalendarUnit returns *true*. A date unit is a Temporal unit for which the corresponding "Category" value in is ~date~, and a time unit is a Temporal unit for which the corresponding "Category" value is ~time~.

Temporal units by descending magnitude
Value Singular property name Plural property name Category Length in nanoseconds Maximum duration rounding increment
~year~ *"year"* *"years"* ~date~ calendar-dependent ~unset~
~month~ *"month"* *"months"* ~date~ calendar-dependent ~unset~
~week~ *"week"* *"weeks"* ~date~ calendar-dependent ~unset~
~day~ *"day"* *"days"* ~date~ nsPerDay ~unset~
~hour~ *"hour"* *"hours"* ~time~ 3.6 × 1012 24
~minute~ *"minute"* *"minutes"* ~time~ 6 × 1010 60
~second~ *"second"* *"seconds"* ~time~ 109 60
~millisecond~ *"millisecond"* *"milliseconds"* ~time~ 106 1000
~microsecond~ *"microsecond"* *"microseconds"* ~time~ 103 1000
~nanosecond~ *"nanosecond"* *"nanoseconds"* ~time~ 1 1000
The length of a day is given as nsPerDay in the table. Note that changes in the UTC offset of a time zone may result in longer or shorter days, so care should be taken when using this value in the context of `Temporal.ZonedDateTime`.

GetTemporalOverflowOption ( _options_: an Object, ): either a normal completion containing either ~constrain~ or ~reject~, or a throw completion

description
It fetches and validates the *"overflow"* property of _options_, returning a default if absent.
1. Let _stringValue_ be ? GetOption(_options_, *"overflow"*, ~string~, « *"constrain"*, *"reject"* », *"constrain"*). 1. If _stringValue_ is *"constrain"*, return ~constrain~. 1. Return ~reject~.

GetTemporalDisambiguationOption ( _options_: an Object, ): either a normal completion containing either ~compatible~, ~earlier~, ~later~, or ~reject~, or a throw completion

description
It fetches and validates the *"disambiguation"* property of _options_, returning a default if absent.
1. Let _stringValue_ be ? GetOption(_options_, *"disambiguation"*, ~string~, « *"compatible"*, *"earlier"*, *"later"*, *"reject"* », *"compatible"*). 1. If _stringValue_ is *"compatible"*, return ~compatible~. 1. If _stringValue_ is *"earlier"*, return ~earlier~. 1. If _stringValue_ is *"later"*, return ~later~. 1. Return ~reject~.

NegateRoundingMode ( _roundingMode_: a rounding mode, ): a rounding mode

description
It returns the correct rounding mode to use when rounding the negative of a value that was originally given with _roundingMode_.
1. If _roundingMode_ is ~ceil~, return ~floor~. 1. If _roundingMode_ is ~floor~, return ~ceil~. 1. If _roundingMode_ is ~half-ceil~, return ~half-floor~. 1. If _roundingMode_ is ~half-floor~, return ~half-ceil~. 1. Return _roundingMode_.

GetTemporalOffsetOption ( _options_: an Object, _fallback_: ~prefer~, ~use~, ~ignore~, or ~reject~, ): either a normal completion containing either ~prefer~, ~use~, ~ignore~, or ~reject~, or a throw completion

description
It fetches and validates the *"offset"* property of _options_, returning _fallback_ as a default if absent.
1. If _fallback_ is ~prefer~, let _stringFallback_ be *"prefer"*. 1. Else if _fallback_ is ~use~, let _stringFallback_ be *"use"*. 1. Else if _fallback_ is ~ignore~, let _stringFallback_ be *"ignore"*. 1. Else, let _stringFallback_ be *"reject"*. 1. Let _stringValue_ be ? GetOption(_options_, *"offset"*, ~string~, « *"prefer"*, *"use"*, *"ignore"*, *"reject"* », _stringFallback_). 1. If _stringValue_ is *"prefer"*, return ~prefer~. 1. If _stringValue_ is *"use"*, return ~use~. 1. If _stringValue_ is *"ignore"*, return ~ignore~. 1. Return ~reject~.

GetTemporalShowCalendarNameOption ( _options_: an Object, ): either a normal completion containing either ~auto~, ~always~, ~never~, or ~critical~, or a throw completion

description
It fetches and validates the *"calendarName"* property from _options_, returning a default if absent.
This property is used in `toString` methods in Temporal to control whether a calendar annotation should be output. 1. Let _stringValue_ be ? GetOption(_options_, *"calendarName"*, ~string~, « *"auto"*, *"always"*, *"never"*, *"critical"* », *"auto"*). 1. If _stringValue_ is *"always"*, return ~always~. 1. If _stringValue_ is *"never"*, return ~never~. 1. If _stringValue_ is *"critical"*, return ~critical~. 1. Return ~auto~.

GetTemporalShowTimeZoneNameOption ( _options_: an Object, ): either a normal completion containing either ~auto~, ~never~, or ~critical~, or a throw completion

description
It fetches and validates the *"timeZoneName"* property from _options_, returning a default if absent.
This property is used in `Temporal.ZonedDateTime.prototype.toString()`. It is different from the `timeZone` property passed to `Temporal.ZonedDateTime.from()` and from the `timeZone` property in the options passed to `Temporal.Instant.prototype.toString()`. 1. Let _stringValue_ be ? GetOption(_options_, *"timeZoneName"*, ~string~, « *"auto"*, *"never"*, *"critical"* », *"auto"*). 1. If _stringValue_ is *"never"*, return ~never~. 1. If _stringValue_ is *"critical"*, return ~critical~. 1. Return ~auto~.

GetTemporalShowOffsetOption ( _options_: an Object, ): either a normal completion containing either ~auto~ or ~never~, or a throw completion

description
It fetches and validates the *"offset"* property from _options_, returning a default if absent.
This property is used in `Temporal.ZonedDateTime.prototype.toString()`. It is different from the `offset` property passed to `Temporal.ZonedDateTime.from()`. 1. Let _stringValue_ be ? GetOption(_options_, *"offset"*, ~string~, « *"auto"*, *"never"* », *"auto"*). 1. If _stringValue_ is *"never"*, return ~never~. 1. Return ~auto~.

GetDirectionOption ( _options_: an Object, ): either a normal completion containing either ~next~ or ~previous~, or a throw completion

description
It fetches and validates the *"direction"* property from _options_, throwing if absent.
1. Let _stringValue_ be ? GetOption(_options_, *"direction"*, ~string~, « *"next"*, *"previous"* », ~required~). 1. If _stringValue_ is *"next"*, return ~next~. 1. Return ~previous~.

ValidateTemporalRoundingIncrement ( _increment_: a positive integer, _dividend_: a positive integer, _inclusive_: a Boolean, ): either a normal completion containing ~unused~ or a throw completion

description
It verifies that _increment_ evenly divides _dividend_, otherwise throwing a *RangeError*. _dividend_ must be divided into more than one part unless _inclusive_ is *true*.
1. If _inclusive_ is *true*, then 1. Let _maximum_ be _dividend_. 1. Else, 1. Assert: _dividend_ > 1. 1. Let _maximum_ be _dividend_ - 1. 1. If _increment_ > _maximum_, throw a *RangeError* exception. 1. If _dividend_ modulo _increment_ ≠ 0, then 1. Throw a *RangeError* exception. 1. Return ~unused~.

GetTemporalFractionalSecondDigitsOption ( _options_: an Object, ): either a normal completion containing either ~auto~ or an integer in the inclusive interval from 0 to 9, or a throw completion

description
It fetches and validates the *"fractionalSecondDigits"* property from _options_, returning a default if absent.
1. Let _digitsValue_ be ? Get(_options_, *"fractionalSecondDigits"*). 1. If _digitsValue_ is *undefined*, return ~auto~. 1. If _digitsValue_ is not a Number, then 1. If ? ToString(_digitsValue_) is not *"auto"*, throw a *RangeError* exception. 1. Return ~auto~. 1. If _digitsValue_ is *NaN*, *+∞*𝔽, or *-∞*𝔽, throw a *RangeError* exception. 1. Let _digitCount_ be floor(ℝ(_digitsValue_)). 1. If _digitCount_ < 0 or _digitCount_ > 9, throw a *RangeError* exception. 1. Return _digitCount_.

ToSecondsStringPrecisionRecord ( _smallestUnit_: ~minute~, ~second~, ~millisecond~, ~microsecond~, ~nanosecond~, or ~unset~, _fractionalDigitCount_: ~auto~ or an integer in the inclusive interval from 0 to 9, ): a Record with fields [[Precision]] (~minute~, ~auto~, or an integer in the inclusive interval from 0 to 9), [[Unit]] (~minute~, ~second~, ~millisecond~, ~microsecond~, or ~nanosecond~), and [[Increment]] (1, 10, or 100)

description
The returned Record represents details for serializing minutes and seconds to a string subject to the specified _smallestUnit_ or (when _smallestUnit_ is ~unset~) _fractionalDigitCount_ digits after the decimal point in the seconds. Its [[Precision]] field is either that count of digits, the value ~auto~ signifying that there should be no insignificant trailing zeroes, or the value ~minute~ signifying that seconds should not be included at all. Its [[Unit]] field is the most precise unit that can contribute to the string, and its [[Increment]] field indicates the rounding increment that should be applied to that unit.
1. If _smallestUnit_ is ~minute~, then 1. Return the Record { [[Precision]]: ~minute~, [[Unit]]: ~minute~, [[Increment]]: 1 }. 1. If _smallestUnit_ is ~second~, then 1. Return the Record { [[Precision]]: 0, [[Unit]]: ~second~, [[Increment]]: 1 }. 1. If _smallestUnit_ is ~millisecond~, then 1. Return the Record { [[Precision]]: 3, [[Unit]]: ~millisecond~, [[Increment]]: 1 }. 1. If _smallestUnit_ is ~microsecond~, then 1. Return the Record { [[Precision]]: 6, [[Unit]]: ~microsecond~, [[Increment]]: 1 }. 1. If _smallestUnit_ is ~nanosecond~, then 1. Return the Record { [[Precision]]: 9, [[Unit]]: ~nanosecond~, [[Increment]]: 1 }. 1. Assert: _smallestUnit_ is ~unset~. 1. If _fractionalDigitCount_ is ~auto~, then 1. Return the Record { [[Precision]]: ~auto~, [[Unit]]: ~nanosecond~, [[Increment]]: 1 }. 1. If _fractionalDigitCount_ = 0, then 1. Return the Record { [[Precision]]: 0, [[Unit]]: ~second~, [[Increment]]: 1 }. 1. If _fractionalDigitCount_ is in the inclusive interval from 1 to 3, then 1. Return the Record { [[Precision]]: _fractionalDigitCount_, [[Unit]]: ~millisecond~, [[Increment]]: 103 - _fractionalDigitCount_ }. 1. If _fractionalDigitCount_ is in the inclusive interval from 4 to 6, then 1. Return the Record { [[Precision]]: _fractionalDigitCount_, [[Unit]]: ~microsecond~, [[Increment]]: 106 - _fractionalDigitCount_ }. 1. Assert: _fractionalDigitCount_ is in the inclusive interval from 7 to 9. 1. Return the Record { [[Precision]]: _fractionalDigitCount_, [[Unit]]: ~nanosecond~, [[Increment]]: 109 - _fractionalDigitCount_ }.

GetTemporalUnitValuedOption ( _options_: an Object, _key_: a property key, _unitGroup_: ~date~, ~time~, or ~datetime~, _default_: ~required~, ~unset~, ~auto~, or a Temporal unit, optional _extraValues_: a List of either Temporal units or ~auto~, ): either a normal completion containing either a Temporal unit, ~unset~, or ~auto~, or a throw completion

description
It attempts to read from the specified property of _options_ a Temporal unit that is covered by the union of _unitGroup_ and _extraValues_, substituting _default_ if the property value is *undefined*.

Both singular and plural unit names are accepted, but only the singular form is used internally.

1. Let _allowedValues_ be a new empty List. 1. For each row of , except the header row, in table order, do 1. Let _unit_ be the value in the "Value" column of the row. 1. If the "Category" column of the row is ~date~ and _unitGroup_ is ~date~ or ~datetime~, append _unit_ to _allowedValues_. 1. Else if the "Category" column of the row is ~time~ and _unitGroup_ is ~time~ or ~datetime~, append _unit_ to _allowedValues_. 1. If _extraValues_ is present, then 1. Set _allowedValues_ to the list-concatenation of _allowedValues_ and _extraValues_. 1. If _default_ is ~unset~, then 1. Let _defaultValue_ be *undefined*. 1. Else if _default_ is ~required~, then 1. Let _defaultValue_ be ~required~. 1. Else if _default_ is ~auto~, then 1. Append _default_ to _allowedValues_. 1. Let _defaultValue_ be *"auto"*. 1. Else, 1. Assert: _allowedValues_ contains _default_. 1. Let _defaultValue_ be the value in the "Singular property name" column of corresponding to the row with _default_ in the "Value" column. 1. Let _allowedStrings_ be a new empty List. 1. For each element _value_ of _allowedValues_, do 1. If _value_ is ~auto~, then 1. Append *"auto"* to _allowedStrings_. 1. Else, 1. Let _singularName_ be the value in the "Singular property name" column of corresponding to the row with _value_ in the "Value" column. 1. Append _singularName_ to _allowedStrings_. 1. Let _pluralName_ be the value in the "Plural property name" column of the corresponding row. 1. Append _pluralName_ to _allowedStrings_. 1. NOTE: For each singular Temporal unit name that is contained within _allowedStrings_, the corresponding plural name is also contained within it. 1. Let _value_ be ? GetOption(_options_, _key_, ~string~, _allowedStrings_, _defaultValue_). 1. If _value_ is *undefined*, return ~unset~. 1. If _value_ is *"auto"*, return ~auto~. 1. Return the value in the "Value" column of corresponding to the row with _value_ in its "Singular property name" or "Plural property name" column.

GetTemporalRelativeToOption ( _options_: an Object, ): either a normal completion containing a Record with fields [[PlainRelativeTo]] (a Temporal.PlainDate or *undefined*) and [[ZonedRelativeTo]] (a Temporal.ZonedDateTime or *undefined*), or a throw completion

description
It examines the value of the `relativeTo` property of its _options_ argument. If the value is *undefined*, both the [[PlainRelativeTo]] and [[ZonedRelativeTo]] fields of the returned Record are *undefined*. If the value is not a String or an Object, it throws a *TypeError*. Otherwise, it attempts to return a Temporal.ZonedDateTime instance in the [[ZonedRelativeTo]] field, or a Temporal.PlainDate instance in the [[PlainRelativeTo]] field, in order of preference, by converting the value. If neither of those are possible, it throws a *RangeError*.
1. Let _value_ be ? Get(_options_, *"relativeTo"*). 1. If _value_ is *undefined*, return the Record { [[PlainRelativeTo]]: *undefined*, [[ZonedRelativeTo]]: *undefined* }. 1. Let _offsetBehaviour_ be ~option~. 1. Let _matchBehaviour_ be ~match-exactly~. 1. If _value_ is an Object, then 1. If _value_ has an [[InitializedTemporalZonedDateTime]] internal slot, then 1. Return the Record { [[PlainRelativeTo]]: *undefined*, [[ZonedRelativeTo]]: _value_ }. 1. If _value_ has an [[InitializedTemporalDate]] internal slot, then 1. Return the Record { [[PlainRelativeTo]]: _value_, [[ZonedRelativeTo]]: *undefined* }. 1. If _value_ has an [[InitializedTemporalDateTime]] internal slot, then 1. Let _plainDate_ be ! CreateTemporalDate(_value_.[[ISODateTime]].[[ISODate]], _value_.[[Calendar]]). 1. Return the Record { [[PlainRelativeTo]]: _plainDate_, [[ZonedRelativeTo]]: *undefined* }. 1. Let _calendar_ be ? GetTemporalCalendarIdentifierWithISODefault(_value_). 1. Let _fields_ be ? PrepareCalendarFields(_calendar_, _value_, « ~year~, ~month~, ~month-code~, ~day~ », « ~hour~, ~minute~, ~second~, ~millisecond~, ~microsecond~, ~nanosecond~, ~offset~, ~time-zone~ », «»). 1. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, ~constrain~). 1. Let _timeZone_ be _fields_.[[TimeZone]]. 1. Let _offsetString_ be _fields_.[[OffsetString]]. 1. If _offsetString_ is ~unset~, then 1. Set _offsetBehaviour_ to ~wall~. 1. Let _isoDate_ be _result_.[[ISODate]]. 1. Let _time_ be _result_.[[Time]]. 1. Else, 1. If _value_ is not a String, throw a *TypeError* exception. 1. Let _result_ be ? ParseISODateTime(_value_, « |TemporalDateTimeString[+Zoned]|, |TemporalDateTimeString[~Zoned]| »). 1. Let _offsetString_ be _result_.[[TimeZone]].[[OffsetString]]. 1. Let _annotation_ be _result_.[[TimeZone]].[[TimeZoneAnnotation]]. 1. If _annotation_ is ~empty~, then 1. Let _timeZone_ be ~unset~. 1. Else, 1. Let _timeZone_ be ? ToTemporalTimeZoneIdentifier(_annotation_). 1. If _result_.[[TimeZone]].[[Z]] is *true*, then 1. Set _offsetBehaviour_ to ~exact~. 1. Else if _offsetString_ is ~empty~, then 1. Set _offsetBehaviour_ to ~wall~. 1. Set _matchBehaviour_ to ~match-minutes~. 1. Let _calendar_ be _result_.[[Calendar]]. 1. If _calendar_ is ~empty~, set _calendar_ to *"iso8601"*. 1. Set _calendar_ to ? CanonicalizeCalendar(_calendar_). 1. Let _isoDate_ be CreateISODateRecord(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]). 1. Let _time_ be _result_.[[Time]]. 1. If _timeZone_ is ~unset~, then 1. Let _plainDate_ be ? CreateTemporalDate(_isoDate_, _calendar_). 1. Return the Record { [[PlainRelativeTo]]: _plainDate_, [[ZonedRelativeTo]]: *undefined* }. 1. If _offsetBehaviour_ is ~option~, then 1. Let _offsetNs_ be ! ParseDateTimeUTCOffset(_offsetString_). 1. Else, 1. Let _offsetNs_ be 0. 1. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_isoDate_, _time_, _offsetBehaviour_, _offsetNs_, _timeZone_, ~compatible~, ~reject~, _matchBehaviour_). 1. Let _zonedRelativeTo_ be ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZone_, _calendar_). 1. Return the Record { [[PlainRelativeTo]]: *undefined*, [[ZonedRelativeTo]]: _zonedRelativeTo_ }.

LargerOfTwoTemporalUnits ( _u1_: a Temporal unit, _u2_: a Temporal unit, ): a Temporal unit

description
Given two Temporal units, it returns the larger of the two units.
1. For each row of , except the header row, in table order, do 1. Let _unit_ be the value in the "Value" column of the row. 1. If _u1_ is _unit_, return _unit_. 1. If _u2_ is _unit_, return _unit_.

IsCalendarUnit ( _unit_: a Temporal unit, ): a Boolean

description
It returns whether _unit_ is a Temporal unit for which rounding would require calendar calculations.
1. If _unit_ is ~year~, return *true*. 1. If _unit_ is ~month~, return *true*. 1. If _unit_ is ~week~, return *true*. 1. Return *false*.

TemporalUnitCategory ( _unit_: a Temporal unit, ): ~date~ or ~time~

description
It returns the category (date or time) of the Temporal unit _unit_.
1. Return the value from the "Category" column of the row of in which _unit_ is in the "Value" column.

MaximumTemporalDurationRoundingIncrement ( _unit_: a Temporal unit, ): 24, 60, 1000, or ~unset~

description
Given a string representing a Temporal.Duration unit, it returns the maximum rounding increment for that unit, or ~unset~ if there is no maximum.
1. Return the value from the "Maximum duration rounding increment" column of the row of in which _unit_ is in the "Value" column.

IsPartialTemporalObject ( _value_: an ECMAScript language value, ): either a normal completion containing a Boolean or a throw completion

description
It determines whether _value_ is a suitable input for one of the Temporal objects' `with()` methods: it must be an Object, it must not be an instance of one of the time-related or date-related Temporal types, and it must not have a `calendar` or `timeZone` property.
1. If _value_ is not an Object, return *false*. 1. If _value_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, return *false*. 1. Let _calendarProperty_ be ? Get(_value_, *"calendar"*). 1. If _calendarProperty_ is not *undefined*, return *false*. 1. Let _timeZoneProperty_ be ? Get(_value_, *"timeZone"*). 1. If _timeZoneProperty_ is not *undefined*, return *false*. 1. Return *true*.

FormatFractionalSeconds ( _subSecondNanoseconds_: an integer in the inclusive interval from 0 to 999999999, _precision_: either an integer in the inclusive interval from 0 to 9 or ~auto~, ): a String

description
If _precision_ is zero, or if _precision_ is ~auto~ and _subSecondNanoseconds_ is zero, then an empty String will be returned. Otherwise, the output will be a decimal point followed by a sequence of fractional seconds digits, truncated to _precision_ digits or (if _precision_ is ~auto~) to the last non-zero digit.
1. If _precision_ is ~auto~, then 1. If _subSecondNanoseconds_ = 0, return the empty String. 1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9). 1. Set _fractionString_ to the longest prefix of _fractionString_ ending with a code unit other than 0x0030 (DIGIT ZERO). 1. Else, 1. If _precision_ = 0, return the empty String. 1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9). 1. Set _fractionString_ to the substring of _fractionString_ from 0 to _precision_. 1. Return the string-concatenation of the code unit 0x002E (FULL STOP) and _fractionString_.

FormatTimeString ( _hour_: an integer in the inclusive interval from 0 to 23, _minute_: an integer in the inclusive interval from 0 to 59, _second_: an integer in the inclusive interval from 0 to 59, _subSecondNanoseconds_: an integer in the inclusive interval from 0 to 999999999, _precision_: an integer in the inclusive interval from 0 to 9, ~minute~, or ~auto~, optional _style_: ~separated~ or ~unseparated~, ): a String

description
It formats a collection of unsigned time components into a string, truncating units as necessary, and separating hours, minutes, and seconds with colons unless _style_ is ~unseparated~. The output will be formatted like HH:MM or HHMM if _precision_ is ~minute~. Otherwise, the output will be formatted like HH:MM:SS or HHMMSS if _precision_ is zero, or if _subSecondNanoseconds_ is zero and _precision_ is ~auto~. Otherwise, the output will be formatted like HH:MM:SS.fff or HHMMSS.fff where "fff" is a sequence of fractional seconds digits, truncated to _precision_ digits or (if _precision_ is ~auto~) to the last non-zero digit.
1. If _style_ is present and _style_ is ~unseparated~, let _separator_ be the empty String; otherwise, let _separator_ be *":"*. 1. Let _hh_ be ToZeroPaddedDecimalString(_hour_, 2). 1. Let _mm_ be ToZeroPaddedDecimalString(_minute_, 2). 1. If _precision_ is ~minute~, return the string-concatenation of _hh_, _separator_, and _mm_. 1. Let _ss_ be ToZeroPaddedDecimalString(_second_, 2). 1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision_). 1. Return the string-concatenation of _hh_, _separator_, _mm_, _separator_, _ss_, and _subSecondsPart_.

GetUnsignedRoundingMode ( _roundingMode_: a rounding mode, _sign_: ~negative~ or ~positive~, ): an unsigned rounding mode

description
It returns the rounding mode that should be applied to the absolute value of a number to produce the same result as if _roundingMode_ were applied to the signed value of the number (negative if _sign_ is ~negative~, or positive otherwise).
1. Return the specification type in the "Unsigned Rounding Mode" column of for the row where the value in the "Rounding Mode" column is _roundingMode_ and the value in the "Sign" column is _sign_. Conversion from rounding mode to unsigned rounding mode
Rounding Mode Sign Unsigned Rounding Mode
~ceil~ ~positive~ ~infinity~
~negative~ ~zero~
~floor~ ~positive~ ~zero~
~negative~ ~infinity~
~expand~ ~positive~ ~infinity~
~negative~ ~infinity~
~trunc~ ~positive~ ~zero~
~negative~ ~zero~
~half-ceil~ ~positive~ ~half-infinity~
~negative~ ~half-zero~
~half-floor~ ~positive~ ~half-zero~
~negative~ ~half-infinity~
~half-expand~ ~positive~ ~half-infinity~
~negative~ ~half-infinity~
~half-trunc~ ~positive~ ~half-zero~
~negative~ ~half-zero~
~half-even~ ~positive~ ~half-even~
~negative~ ~half-even~

ApplyUnsignedRoundingMode ( _x_: a mathematical value, _r1_: a mathematical value, _r2_: a mathematical value, _unsignedRoundingMode_: a specification type from the "Unsigned Rounding Mode" column of , or *undefined*, ): a mathematical value

description
It considers _x_, bracketed below by _r1_ and above by _r2_, and returns either _r1_ or _r2_ according to _unsignedRoundingMode_.
1. If _x_ = _r1_, return _r1_. 1. Assert: _r1_ < _x_ < _r2_. 1. Assert: _unsignedRoundingMode_ is not *undefined*. 1. If _unsignedRoundingMode_ is ~zero~, return _r1_. 1. If _unsignedRoundingMode_ is ~infinity~, return _r2_. 1. Let _d1_ be _x_ – _r1_. 1. Let _d2_ be _r2_ – _x_. 1. If _d1_ < _d2_, return _r1_. 1. If _d2_ < _d1_, return _r2_. 1. Assert: _d1_ is equal to _d2_. 1. If _unsignedRoundingMode_ is ~half-zero~, return _r1_. 1. If _unsignedRoundingMode_ is ~half-infinity~, return _r2_. 1. Assert: _unsignedRoundingMode_ is ~half-even~. 1. Let _cardinality_ be (_r1_ / (_r2_ – _r1_)) modulo 2. 1. If _cardinality_ = 0, return _r1_. 1. Return _r2_.

RoundNumberToIncrement ( _x_: a mathematical value, _increment_: a positive integer, _roundingMode_: a rounding mode, ): an integer

description
It rounds _x_ to the nearest multiple of _increment_, up or down according to _roundingMode_.
1. Let _quotient_ be _x_ / _increment_. 1. If _quotient_ < 0, then 1. Let _isNegative_ be ~negative~. 1. Set _quotient_ to -_quotient_. 1. Else, 1. Let _isNegative_ be ~positive~. 1. Let _unsignedRoundingMode_ be GetUnsignedRoundingMode(_roundingMode_, _isNegative_). 1. Let _r1_ be the largest integer such that _r1_ ≤ _quotient_. 1. Let _r2_ be the smallest integer such that _r2_ > _quotient_. 1. Let _rounded_ be ApplyUnsignedRoundingMode(_quotient_, _r1_, _r2_, _unsignedRoundingMode_). 1. If _isNegative_ is ~negative~, set _rounded_ to -_rounded_. 1. Return _rounded_ × _increment_.

RoundNumberToIncrementAsIfPositive ( _x_: a mathematical value, _increment_: a positive integer, _roundingMode_: a rounding mode, ): an integer

description
It rounds _x_ to the nearest multiple of _increment_, up or down according to _roundingMode_, but always as if _x_ were positive. For example, ~floor~ and ~trunc~ behave identically. This is used when rounding exact times, where "rounding down" conceptually always means towards the beginning of time, even if the time is expressed as a negative amount of time relative to an epoch.
1. Let _quotient_ be _x_ / _increment_. 1. Let _unsignedRoundingMode_ be GetUnsignedRoundingMode(_roundingMode_, ~positive~). 1. Let _r1_ be the largest integer such that _r1_ ≤ _quotient_. 1. Let _r2_ be the smallest integer such that _r2_ > _quotient_. 1. Let _rounded_ be ApplyUnsignedRoundingMode(_quotient_, _r1_, _r2_, _unsignedRoundingMode_). 1. Return _rounded_ × _increment_.

RFC 9557 / ISO 8601 grammar

Several operations in this section are intended to parse RFC 9557 strings representing a date, a time, a duration, or a combined date and time. For the purposes of these operations, a valid RFC 9557 string is defined as a string that can be generated by one of the goal elements of the following grammar.

This grammar is adapted from the ABNF grammar of ISO 8601 that is given in appendix A of RFC 3339, augmented with the grammar of annotations in section 4.1 of RFC 9557

RFC 9557 and ISO 8601 are similar, but ISO 8601 defines a number of optional deviations that are allowed "by agreement between the communicating parties". The following is a list of deviations supported by this grammar:

In addition to the above deviations, any number of conforming RFC 9557 suffixes in square brackets are allowed. However, the only recognized suffixes are time zone and BCP 47 calendar. Others are ignored, unless they are prefixed with `!`, in which case they are rejected. Note that the suffix keys, although they look similar, are not the same as keys in RFC 6067. In particular, keys are lowercase-only.

Alpha ::: one of `A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` `N` `O` `P` `Q` `R` `S` `T` `U` `V` `W` `X` `Y` `Z` `a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` LowercaseAlpha ::: one of `a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` DateSeparator[Extended] ::: [+Extended] `-` [~Extended] [empty] DaysDesignator ::: one of `D` `d` HoursDesignator ::: one of `H` `h` MinutesDesignator ::: one of `M` `m` MonthsDesignator ::: one of `M` `m` DurationDesignator ::: one of `P` `p` SecondsDesignator ::: one of `S` `s` DateTimeSeparator ::: <SP> `T` `t` TimeDesignator ::: one of `T` `t` WeeksDesignator ::: one of `W` `w` YearsDesignator ::: one of `Y` `y` UTCDesignator ::: one of `Z` `z` AnnotationCriticalFlag ::: `!` DateYear ::: DecimalDigit DecimalDigit DecimalDigit DecimalDigit ASCIISign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DateMonth ::: `0` NonZeroDigit `10` `11` `12` DateDay ::: `0` NonZeroDigit `1` DecimalDigit `2` DecimalDigit `30` `31` DateSpecYearMonth ::: DateYear DateSeparator[+Extended] DateMonth DateYear DateSeparator[~Extended] DateMonth DateSpecMonthDay ::: `--`? DateMonth DateSeparator[+Extended] DateDay `--`? DateMonth DateSeparator[~Extended] DateDay DateSpec[Extended] ::: DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay Date ::: DateSpec[+Extended] DateSpec[~Extended] TimeSecond ::: MinuteSecond `60` NormalizedUTCOffset ::: ASCIISign Hour TimeSeparator[+Extended] MinuteSecond UTCOffset[SubMinutePrecision] ::: ASCIISign Hour ASCIISign Hour TimeSeparator[+Extended] MinuteSecond ASCIISign Hour TimeSeparator[~Extended] MinuteSecond [+SubMinutePrecision] ASCIISign Hour TimeSeparator[+Extended] MinuteSecond TimeSeparator[+Extended] MinuteSecond TemporalDecimalFraction? [+SubMinutePrecision] ASCIISign Hour TimeSeparator[~Extended] MinuteSecond TimeSeparator[~Extended] MinuteSecond TemporalDecimalFraction? DateTimeUTCOffset[Z] ::: [+Z] UTCDesignator UTCOffset[+SubMinutePrecision] TZLeadingChar ::: Alpha `.` `_` TZChar ::: TZLeadingChar DecimalDigit `-` `+` TimeZoneIANANameComponent ::: TZLeadingChar TimeZoneIANANameComponent TZChar TimeZoneIANAName ::: TimeZoneIANANameComponent TimeZoneIANAName `/` TimeZoneIANANameComponent TimeZoneIdentifier ::: UTCOffset[~SubMinutePrecision] TimeZoneIANAName TimeZoneAnnotation ::: `[` AnnotationCriticalFlag? TimeZoneIdentifier `]` AKeyLeadingChar ::: LowercaseAlpha `_` AKeyChar ::: AKeyLeadingChar DecimalDigit `-` AnnotationKey ::: AKeyLeadingChar AnnotationKey AKeyChar AnnotationValueComponent ::: Alpha AnnotationValueComponent? DecimalDigit AnnotationValueComponent? AnnotationValue ::: AnnotationValueComponent AnnotationValueComponent `-` AnnotationValue Annotation ::: `[` AnnotationCriticalFlag? AnnotationKey `=` AnnotationValue `]` Annotations ::: Annotation Annotations? TimeSpec[Extended] ::: Hour Hour TimeSeparator[?Extended] MinuteSecond Hour TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] TimeSecond TemporalDecimalFraction? Time ::: TimeSpec[+Extended] TimeSpec[~Extended] DateTime[Z, TimeRequired] ::: [~TimeRequired] Date Date DateTimeSeparator Time DateTimeUTCOffset[?Z]? AnnotatedTime ::: TimeDesignator Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations? AnnotatedDateTime[Zoned, TimeRequired] ::: [~Zoned] DateTime[~Z, ?TimeRequired] TimeZoneAnnotation? Annotations? [+Zoned] DateTime[+Z, ?TimeRequired] TimeZoneAnnotation Annotations? AnnotatedYearMonth ::: DateSpecYearMonth TimeZoneAnnotation? Annotations? AnnotatedMonthDay ::: DateSpecMonthDay TimeZoneAnnotation? Annotations? DurationSecondsPart ::: DecimalDigits[~Sep] TemporalDecimalFraction? SecondsDesignator DurationMinutesPart ::: DecimalDigits[~Sep] TemporalDecimalFraction MinutesDesignator DecimalDigits[~Sep] MinutesDesignator DurationSecondsPart? DurationHoursPart ::: DecimalDigits[~Sep] TemporalDecimalFraction HoursDesignator DecimalDigits[~Sep] HoursDesignator DurationMinutesPart DecimalDigits[~Sep] HoursDesignator DurationSecondsPart? DurationTime ::: TimeDesignator DurationHoursPart TimeDesignator DurationMinutesPart TimeDesignator DurationSecondsPart DurationDaysPart ::: DecimalDigits[~Sep] DaysDesignator DurationWeeksPart ::: DecimalDigits[~Sep] WeeksDesignator DurationDaysPart? DurationMonthsPart ::: DecimalDigits[~Sep] MonthsDesignator DurationWeeksPart DecimalDigits[~Sep] MonthsDesignator DurationDaysPart? DurationYearsPart ::: DecimalDigits[~Sep] YearsDesignator DurationMonthsPart DecimalDigits[~Sep] YearsDesignator DurationWeeksPart DecimalDigits[~Sep] YearsDesignator DurationDaysPart? DurationDate ::: DurationYearsPart DurationTime? DurationMonthsPart DurationTime? DurationWeeksPart DurationTime? DurationDaysPart DurationTime? Duration ::: ASCIISign? DurationDesignator DurationDate ASCIISign? DurationDesignator DurationTime TemporalInstantString ::: Date DateTimeSeparator Time DateTimeUTCOffset[+Z] TimeZoneAnnotation? Annotations? TemporalDateTimeString[Zoned] ::: AnnotatedDateTime[?Zoned, ~TimeRequired] TemporalDurationString ::: Duration TemporalMonthDayString ::: AnnotatedMonthDay AnnotatedDateTime[~Zoned, ~TimeRequired] TemporalTimeString ::: AnnotatedTime AnnotatedDateTime[~Zoned, +TimeRequired] TemporalYearMonthString ::: AnnotatedYearMonth AnnotatedDateTime[~Zoned, ~TimeRequired]

Static Semantics: IsValidMonthDay ( ): a Boolean

DateSpec[Extended] ::: DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay DateSpecMonthDay ::: `--`? DateMonth DateSeparator[+Extended] DateDay `--`? DateMonth DateSeparator[~Extended] DateDay 1. If |DateDay| is *"31"* and |DateMonth| is *"02"*, *"04"*, *"06"*, *"09"*, *"11"*, return *false*. 1. If |DateMonth| is *"02"* and |DateDay| is *"30"*, return *false*. 1. Return *true*.

Static Semantics: IsValidDate ( ): a Boolean

DateSpec[Extended] ::: DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay 1. If IsValidMonthDay of |DateSpec| is *false*, return *false*. 1. Let _year_ be ℝ(StringToNumber(CodePointsToString(|DateYear|))). 1. If |DateMonth| is *"02"* and |DateDay| is *"29"* and MathematicalInLeapYear(EpochTimeForYear(_year_)) = 0, return *false*. 1. Return *true*.

Static Semantics: Early Errors

AnnotatedTime ::: Time DateTimeUTCOffset[~Z]? TimeZoneAnnotation? Annotations?
  • It is a Syntax Error if ParseText(|Time| |DateTimeUTCOffset[~Z]|, |DateSpecMonthDay|) is a Parse Node.
  • It is a Syntax Error if ParseText(|Time| |DateTimeUTCOffset[~Z]|, |DateSpecYearMonth|) is a Parse Node.
DateSpec[Extended] ::: DateYear DateSeparator[?Extended] DateMonth DateSeparator[?Extended] DateDay
  • It is a Syntax Error if IsValidDate of |DateSpec| is *false*.
DateSpecMonthDay ::: `--`? DateMonth DateSeparator[+Extended] DateDay `--`? DateMonth DateSeparator[~Extended] DateDay
  • It is a Syntax Error if IsValidMonthDay of |DateSpecMonthDay| is *false*.
DateYear ::: ASCIISign DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
  • It is a Syntax Error if |DateYear| is *"-000000"*.

ISO String Time Zone Parse Records

A ISO String Time Zone Parse Record is a Record value used to represent the result of parsing the representation of the time zone in an ISO 8601 / RFC 9557 string.

ISO String Time Zone Parse Records have the fields listed in .

Field Name Value Meaning
[[Z]] a Boolean Whether the string contained the `Z` UTC designator.
[[OffsetString]] a String or ~empty~ The UTC offset from the string, or ~empty~ if none was present.
[[TimeZoneAnnotation]] a String or ~empty~ The time zone annotation from the string, or ~empty~ if none was present.

ISO Date-Time Parse Records

An ISO Date-Time Parse Record is a Record value used to represent the result of parsing an ISO 8601 / RFC 9557 string.

For any ISO Date-Time Parse Record _r_, IsValidISODate(_r_.[[Year]], _r_.[[Month]], _r_.[[Day]]) must return *true*, or, if _r_.[[Year]] is ~empty~, IsValidISODate(1972, _r_.[[Month]], _r_.[[Day]]) must return *true*. It is not necessary for the represented date and time to be within the range given by ISODateTimeWithinLimits.

ISO Date-Time Parse Records have the fields listed in .

Field Name Value Meaning
[[Year]] an integer or ~empty~ The year in the ISO 8601 calendar, or ~empty~ if the string's format was that of |TemporalMonthDayString| and the year was omitted.
[[Month]] an integer between 1 and 12, inclusive The number of the month in the ISO 8601 calendar.
[[Day]] an integer between 1 and 31, inclusive The number of the day of the month in the ISO 8601 calendar.
[[Time]] either a Time Record with [[Days]] value 0, or ~start-of-day~ The time of day, or ~start-of-day~ if the time was omitted from the string.
[[TimeZone]] an ISO String Time Zone Parse Record A representation of how the time zone was expressed in the string.
[[Calendar]] a String or ~empty~ The calendar type from the string, or ~empty~ if none was present.

ParseISODateTime ( _isoString_: a String, _allowedFormats_: a List of nonterminals, ): either a normal completion containing an ISO Date-Time Parse Record or a throw completion

description
It parses the argument as an ISO 8601 / RFC 9557 string and returns a Record representing each date and time component as a distinct field.
1. Let _parseResult_ be ~empty~. 1. Let _calendar_ be ~empty~. 1. Let _yearAbsent_ be *false*. 1. For each nonterminal _goal_ of _allowedFormats_, do 1. If _parseResult_ is not a Parse Node, then 1. Set _parseResult_ to ParseText(StringToCodePoints(_isoString_), _goal_). 1. If _parseResult_ is a Parse Node, then 1. Let _calendarWasCritical_ be *false*. 1. For each |Annotation| Parse Node _annotation_ contained within _parseResult_, do 1. Let _key_ be the source text matched by the |AnnotationKey| Parse Node contained within _annotation_. 1. Let _value_ be the source text matched by the |AnnotationValue| Parse Node contained within _annotation_. 1. If CodePointsToString(_key_) is *"u-ca"*, then 1. If _calendar_ is ~empty~, then 1. Set _calendar_ to CodePointsToString(_value_). 1. If _annotation_ contains an |AnnotationCriticalFlag| Parse Node, set _calendarWasCritical_ to *true*. 1. Else, 1. If _annotation_ contains an |AnnotationCriticalFlag| Parse Node, or _calendarWasCritical_ is *true*, throw a *RangeError* exception. 1. Else, 1. If _annotation_ contains an |AnnotationCriticalFlag| Parse Node, throw a *RangeError* exception. 1. If _goal_ is |TemporalMonthDayString| or |TemporalYearMonthString|, _calendar_ is not ~empty~, and the ASCII-lowercase of _calendar_ is not *"iso8601"*, throw a *RangeError* exception. 1. If _goal_ is |TemporalMonthDayString| and _parseResult_ does not contain a |DateYear| Parse Node, set _yearAbsent_ to *true*. 1. If _parseResult_ is not a Parse Node, throw a *RangeError* exception. 1. NOTE: Applications of StringToNumber below do not lose precision, since each of the parsed values is guaranteed to be a sufficiently short string of decimal digits. 1. Let each of _year_, _month_, _day_, _hour_, _minute_, _second_, and _fSeconds_ be the source text matched by the respective |DateYear|, |DateMonth|, |DateDay|, the first |Hour|, the first |MinuteSecond|, |TimeSecond|, and the first |TemporalDecimalFraction| Parse Node contained within _parseResult_, or an empty sequence of code points if not present. 1. Let _yearMV_ be ℝ(StringToNumber(CodePointsToString(_year_))). 1. If _month_ is empty, then 1. Let _monthMV_ be 1. 1. Else, 1. Let _monthMV_ be ℝ(StringToNumber(CodePointsToString(_month_))). 1. If _day_ is empty, then 1. Let _dayMV_ be 1. 1. Else, 1. Let _dayMV_ be ℝ(StringToNumber(CodePointsToString(_day_))). 1. If _hour_ is empty, then 1. Let _hourMV_ be 0. 1. Else, 1. Let _hourMV_ be ℝ(StringToNumber(CodePointsToString(_hour_))). 1. If _minute_ is empty, then 1. Let _minuteMV_ be 0. 1. Else, 1. Let _minuteMV_ be ℝ(StringToNumber(CodePointsToString(_minute_))). 1. If _second_ is empty, then 1. Let _secondMV_ be 0. 1. Else, 1. Let _secondMV_ be ℝ(StringToNumber(CodePointsToString(_second_))). 1. If _secondMV_ = 60, then 1. Set _secondMV_ to 59. 1. If _fSeconds_ is not empty, then 1. Let _fSecondsDigits_ be the substring of CodePointsToString(_fSeconds_) from 1. 1. Let _fSecondsDigitsExtended_ be the string-concatenation of _fSecondsDigits_ and *"000000000"*. 1. Let _millisecond_ be the substring of _fSecondsDigitsExtended_ from 0 to 3. 1. Let _microsecond_ be the substring of _fSecondsDigitsExtended_ from 3 to 6. 1. Let _nanosecond_ be the substring of _fSecondsDigitsExtended_ from 6 to 9. 1. Let _millisecondMV_ be ℝ(StringToNumber(_millisecond_)). 1. Let _microsecondMV_ be ℝ(StringToNumber(_microsecond_)). 1. Let _nanosecondMV_ be ℝ(StringToNumber(_nanosecond_)). 1. Else, 1. Let _millisecondMV_ be 0. 1. Let _microsecondMV_ be 0. 1. Let _nanosecondMV_ be 0. 1. Assert: IsValidISODate(_yearMV_, _monthMV_, _dayMV_) is *true*. 1. If _hour_ is empty, then 1. Let _time_ be ~start-of-day~. 1. Else, 1. Let _time_ be CreateTimeRecord(_hourMV_, _minuteMV_, _secondMV_, _millisecondMV_, _microsecondMV_, _nanosecondMV_). 1. Let _timeZoneResult_ be ISO String Time Zone Parse Record { [[Z]]: *false*, [[OffsetString]]: ~empty~, [[TimeZoneAnnotation]]: ~empty~ }. 1. If _parseResult_ contains a |TimeZoneIdentifier| Parse Node, then 1. Let _identifier_ be the source text matched by the |TimeZoneIdentifier| Parse Node contained within _parseResult_. 1. Set _timeZoneResult_.[[TimeZoneAnnotation]] to CodePointsToString(_identifier_). 1. If _parseResult_ contains a |UTCDesignator| Parse Node, then 1. Set _timeZoneResult_.[[Z]] to *true*. 1. Else if _parseResult_ contains a |UTCOffset[+SubMinutePrecision]| Parse Node, then 1. Let _offset_ be the source text matched by the |UTCOffset[+SubMinutePrecision]| Parse Node contained within _parseResult_. 1. Set _timeZoneResult_.[[OffsetString]] to CodePointsToString(_offset_). 1. If _yearAbsent_ is *true*, let _yearReturn_ be ~empty~; else let _yearReturn_ be _yearMV_. 1. Return ISO Date-Time Parse Record { [[Year]]: _yearReturn_, [[Month]]: _monthMV_, [[Day]]: _dayMV_, [[Time]]: _time_, [[TimeZone]]: _timeZoneResult_, [[Calendar]]: _calendar_ }.

ParseTemporalCalendarString ( _string_: a String, ): either a normal completion containing a String or a throw completion

description
It parses the argument either as an RFC 9557 string or bare calendar type, and returns the calendar type. The returned string is syntactically a valid calendar type, but not necessarily an existing one.
1. Let _parseResult_ be Completion(ParseISODateTime(_string_, « |TemporalDateTimeString[+Zoned]|, |TemporalDateTimeString[~Zoned]|, |TemporalInstantString|, |TemporalTimeString|, |TemporalMonthDayString|, |TemporalYearMonthString| »)). 1. If _parseResult_ is a normal completion, then 1. Let _calendar_ be _parseResult_.[[Value]].[[Calendar]]. 1. If _calendar_ is ~empty~, return *"iso8601"*. 1. Return _calendar_. 1. Set _parseResult_ to ParseText(StringToCodePoints(_string_), |AnnotationValue|). 1. If _parseResult_ is a List of errors, throw a *RangeError* exception. 1. Return _string_.

ParseTemporalDurationString ( _isoString_: a String, ): either a normal completion containing a Temporal.Duration or a throw completion

description
It parses the argument as an ISO 8601 duration string.
The value of ToIntegerWithTruncation(the empty String) is 0. Use of mathematical values rather than approximations is important to avoid off-by-one errors with input like "PT46H66M71.50040904S". 1. Let _duration_ be ParseText(StringToCodePoints(_isoString_), |TemporalDurationString|). 1. If _duration_ is a List of errors, throw a *RangeError* exception. 1. Let _sign_ be the source text matched by the |ASCIISign| Parse Node contained within _duration_, or an empty sequence of code points if not present. 1. If _duration_ contains a |DurationYearsPart| Parse Node, then 1. Let _yearsNode_ be that |DurationYearsPart| Parse Node contained within _duration_. 1. Let _years_ be the source text matched by the |DecimalDigits| Parse Node contained within _yearsNode_. 1. Else, 1. Let _years_ be an empty sequence of code points. 1. If _duration_ contains a |DurationMonthsPart| Parse Node, then 1. Let _monthsNode_ be the |DurationMonthsPart| Parse Node contained within _duration_. 1. Let _months_ be the source text matched by the |DecimalDigits| Parse Node contained within _monthsNode_. 1. Else, 1. Let _months_ be an empty sequence of code points. 1. If _duration_ contains a |DurationWeeksPart| Parse Node, then 1. Let _weeksNode_ be the |DurationWeeksPart| Parse Node contained within _duration_. 1. Let _weeks_ be the source text matched by the |DecimalDigits| Parse Node contained within _weeksNode_. 1. Else, 1. Let _weeks_ be an empty sequence of code points. 1. If _duration_ contains a |DurationDaysPart| Parse Node, then 1. Let _daysNode_ be the |DurationDaysPart| Parse Node contained within _duration_. 1. Let _days_ be the source text matched by the |DecimalDigits| Parse Node contained within _daysNode_. 1. Else, 1. Let _days_ be an empty sequence of code points. 1. If _duration_ contains a |DurationHoursPart| Parse Node, then 1. Let _hoursNode_ be the |DurationHoursPart| Parse Node contained within _duration_. 1. Let _hours_ be the source text matched by the |DecimalDigits| Parse Node contained within _hoursNode_. 1. Let _fHours_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _hoursNode_, or an empty sequence of code points if not present. 1. Else, 1. Let _hours_ be an empty sequence of code points. 1. Let _fHours_ be an empty sequence of code points. 1. If _duration_ contains a |DurationMinutesPart| Parse Node, then 1. Let _minutesNode_ be the |DurationMinutesPart| Parse Node contained within _duration_. 1. Let _minutes_ be the source text matched by the |DecimalDigits| Parse Node contained within _minutesNode_. 1. Let _fMinutes_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _minutesNode_, or an empty sequence of code points if not present. 1. Else, 1. Let _minutes_ be an empty sequence of code points. 1. Let _fMinutes_ be an empty sequence of code points. 1. If _duration_ contains a |DurationSecondsPart| Parse Node, then 1. Let _secondsNode_ be the |DurationSecondsPart| Parse Node contained within _duration_. 1. Let _seconds_ be the source text matched by the |DecimalDigits| Parse Node contained within _secondsNode_. 1. Let _fSeconds_ be the source text matched by the |TemporalDecimalFraction| Parse Node contained within _secondsNode_, or an empty sequence of code points if not present. 1. Else, 1. Let _seconds_ be an empty sequence of code points. 1. Let _fSeconds_ be an empty sequence of code points. 1. Let _yearsMV_ be ? ToIntegerWithTruncation(CodePointsToString(_years_)). 1. Let _monthsMV_ be ? ToIntegerWithTruncation(CodePointsToString(_months_)). 1. Let _weeksMV_ be ? ToIntegerWithTruncation(CodePointsToString(_weeks_)). 1. Let _daysMV_ be ? ToIntegerWithTruncation(CodePointsToString(_days_)). 1. Let _hoursMV_ be ? ToIntegerWithTruncation(CodePointsToString(_hours_)). 1. If _fHours_ is not empty, then 1. Assert: _minutes_, _fMinutes_, _seconds_, and _fSeconds_ are empty. 1. Let _fHoursDigits_ be the substring of CodePointsToString(_fHours_) from 1. 1. Let _fHoursScale_ be the length of _fHoursDigits_. 1. Let _minutesMV_ be ? ToIntegerWithTruncation(_fHoursDigits_) / 10_fHoursScale_ × 60. 1. Else, 1. Let _minutesMV_ be ? ToIntegerWithTruncation(CodePointsToString(_minutes_)). 1. If _fMinutes_ is not empty, then 1. Assert: _seconds_ and _fSeconds_ are empty. 1. Let _fMinutesDigits_ be the substring of CodePointsToString(_fMinutes_) from 1. 1. Let _fMinutesScale_ be the length of _fMinutesDigits_. 1. Let _secondsMV_ be ? ToIntegerWithTruncation(_fMinutesDigits_) / 10_fMinutesScale_ × 60. 1. Else if _seconds_ is not empty, then 1. Let _secondsMV_ be ? ToIntegerWithTruncation(CodePointsToString(_seconds_)). 1. Else, 1. Let _secondsMV_ be remainder(_minutesMV_, 1) × 60. 1. If _fSeconds_ is not empty, then 1. Let _fSecondsDigits_ be the substring of CodePointsToString(_fSeconds_) from 1. 1. Let _fSecondsScale_ be the length of _fSecondsDigits_. 1. Let _millisecondsMV_ be ? ToIntegerWithTruncation(_fSecondsDigits_) / 10_fSecondsScale_ × 1000. 1. Else, 1. Let _millisecondsMV_ be remainder(_secondsMV_, 1) × 1000. 1. Let _microsecondsMV_ be remainder(_millisecondsMV_, 1) × 1000. 1. Let _nanosecondsMV_ be remainder(_microsecondsMV_, 1) × 1000. 1. If _sign_ contains the code point U+002D (HYPHEN-MINUS), then 1. Let _factor_ be -1. 1. Else, 1. Let _factor_ be 1. 1. Set _yearsMV_ to _yearsMV_ × _factor_. 1. Set _monthsMV_ to _monthsMV_ × _factor_. 1. Set _weeksMV_ to _weeksMV_ × _factor_. 1. Set _daysMV_ to _daysMV_ × _factor_. 1. Set _hoursMV_ to _hoursMV_ × _factor_. 1. Set _minutesMV_ to floor(_minutesMV_) × _factor_. 1. Set _secondsMV_ to floor(_secondsMV_) × _factor_. 1. Set _millisecondsMV_ to floor(_millisecondsMV_) × _factor_. 1. Set _microsecondsMV_ to floor(_microsecondsMV_) × _factor_. 1. Set _nanosecondsMV_ to floor(_nanosecondsMV_) × _factor_. 1. Return ? CreateTemporalDuration(_yearsMV_, _monthsMV_, _weeksMV_, _daysMV_, _hoursMV_, _minutesMV_, _secondsMV_, _millisecondsMV_, _microsecondsMV_, _nanosecondsMV_).

ParseTemporalTimeZoneString ( _timeZoneString_: a String, ): either a normal completion containing a Record containing information about the time zone, or a throw completion

description
It parses the argument as either a time zone identifier or an RFC 9557 string. The returned Record's fields are set as follows:
  • If _timeZoneString_ is a named time zone identifier, then [[Name]] is _timeZoneString_ and [[OffsetMinutes]] is ~empty~.
  • Otherwise, if _timeZoneString_ is an offset time zone identifier, then [[OffsetMinutes]] is a signed integer and [[Name]] is ~empty~.
  • Otherwise, if _timeZoneString_ is a string with a time zone annotation containing a named time zone identifier, then [[Name]] is the time zone identifier contained in the annotation and [[OffsetMinutes]] is ~empty~.
  • Otherwise, if _timeZoneString_ is a string with a time zone annotation containing an offset time zone identifier, then [[OffsetMinutes]] is a signed integer and [[Name]] is ~empty~.
  • Otherwise, if _timeZoneString_ is a string using a *Z* offset designator, then [[Name]] is *"UTC"* and [[OffsetMinutes]] is ~empty~.
  • Otherwise, if _timeZoneString_ is a string using a numeric UTC offset, then [[OffsetMinutes]] is a signed integer and [[Name]] is ~empty~.
  • Otherwise, a *RangeError* is thrown.
1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZoneString_), |TimeZoneIdentifier|). 1. If _parseResult_ is a Parse Node, then 1. Return ! ParseTimeZoneIdentifier(_timeZoneString_). 1. Let _result_ be ? ParseISODateTime(_timeZoneString_, « |TemporalDateTimeString[+Zoned]|, |TemporalDateTimeString[~Zoned]|, |TemporalInstantString|, |TemporalTimeString|, |TemporalMonthDayString|, |TemporalYearMonthString| »). 1. Let _timeZoneResult_ be _result_.[[TimeZone]]. 1. If _timeZoneResult_.[[TimeZoneAnnotation]] is not ~empty~, then 1. Return ! ParseTimeZoneIdentifier(_timeZoneResult_.[[TimeZoneAnnotation]]). 1. If _timeZoneResult_.[[Z]] is *true*, then 1. Return ! ParseTimeZoneIdentifier(*"UTC"*). 1. If _timeZoneResult_.[[OffsetString]] is not ~empty~, then 1. Return ? ParseTimeZoneIdentifier(_timeZoneResult_.[[OffsetString]]). 1. Throw a *RangeError* exception.

ToPositiveIntegerWithTruncation ( _argument_: an ECMAScript language value, ): either a normal completion containing a positive integer or a throw completion

description
It converts _argument_ to an integer representing its Number value with fractional part truncated, or throws a *RangeError* when that value is not finite or not positive.
1. Let _integer_ be ? ToIntegerWithTruncation(_argument_). 1. If _integer_ ≤ 0, throw a *RangeError* exception. 1. Return _integer_.

ToIntegerWithTruncation ( _argument_: an ECMAScript language value, ): either a normal completion containing an integer or a throw completion

description
It converts _argument_ to an integer representing its Number value with fractional part truncated, or throws a *RangeError* when that value is not finite.
1. Let _number_ be ? ToNumber(_argument_). 1. If _number_ is *NaN*, *+∞*𝔽 or *-∞*𝔽, throw a *RangeError* exception. 1. Return truncate(ℝ(_number_)).

ToIntegerIfIntegral ( _argument_: an ECMAScript language value, ): either a normal completion containing an integer or a throw completion

description
It converts _argument_ to an integer representing its Number value, or throws a *RangeError* when that value is not integral.
1. Let _number_ be ? ToNumber(_argument_). 1. If _number_ is not an integral Number, throw a *RangeError* exception. 1. Return ℝ(_number_).

ToMonthCode ( _argument_: an ECMAScript language value, ): either a normal completion containing a String or a throw completion

description
It converts _argument_ to a String, or throws a *TypeError* if that is not possible. It also requires that the String is a syntactically valid month code, or throws a *RangeError* if it is not. The month code is not guaranteed to be correct in the context of any particular calendar; for example, some calendars do not have leap months.
1. Let _monthCode_ be ? ToPrimitive(_argument_, ~string~). 1. If _monthCode_ is not a String, throw a *TypeError* exception. 1. If the length of _monthCode_ is not 3 or 4, throw a *RangeError* exception. 1. If the first code unit of _monthCode_ is not 0x004D (LATIN CAPITAL LETTER M), throw a *RangeError* exception. 1. If the second code unit of _monthCode_ is not in the inclusive interval from 0x0030 (DIGIT ZERO) to 0x0039 (DIGIT NINE), throw a *RangeError* exception. 1. If the third code unit of _monthCode_ is not in the inclusive interval from 0x0030 (DIGIT ZERO) to 0x0039 (DIGIT NINE), throw a *RangeError* exception. 1. If the length of _monthCode_ is 4 and the fourth code unit of _monthCode_ is not 0x004C (LATIN CAPITAL LETTER L), throw a *RangeError* exception. 1. Let _monthCodeDigits_ be the substring of monthCode from 1 to 3. 1. Let _monthCodeInteger_ be ℝ(StringToNumber(_monthCodeDigits_)). 1. If _monthCodeInteger_ is 0 and the length of _monthCode_ is not 4, throw a *RangeError* exception. 1. Return _monthCode_.

ToOffsetString ( _argument_: an ECMAScript language value, ): either a normal completion containing a String or a throw completion

description
It converts _argument_ to a String, or throws a *TypeError* if that is not possible. It also requires that the String is parseable as a UTC offset string, or throws a *RangeError* if it is not.
1. Let _offset_ be ? ToPrimitive(_argument_, ~string~). 1. If _offset_ is not a String, throw a *TypeError* exception. 1. Perform ? ParseDateTimeUTCOffset(_offset_). 1. Return _offset_.

ISODateToFields ( _calendar_: a calendar type, _isoDate_: an ISO Date Record, _type_: ~date~, ~year-month~, or ~month-day~, ): a Calendar Fields Record

description
1. Let _fields_ be an empty Calendar Fields Record with all fields set to ~unset~. 1. Let _calendarDate_ be CalendarISOToDate(_calendar_, _isoDate_). 1. Set _fields_.[[MonthCode]] to _calendarDate_.[[MonthCode]]. 1. If _type_ is ~month-day~ or ~date~, then 1. Set _fields_.[[Day]] to _calendarDate_.[[Day]]. 1. If _type_ is ~year-month~ or ~date~, then 1. Set _fields_.[[Year]] to _calendarDate_.[[Year]]. 1. Return _fields_.

GetDifferenceSettings ( _operation_: ~since~ or ~until~, _options_: an Object, _unitGroup_: ~date~, ~time~, or ~datetime~, _disallowedUnits_: a List of Temporal units, _fallbackSmallestUnit_: a Temporal unit, _smallestLargestDefaultUnit_: a Temporal unit, ): either a normal completion containing a Record with fields [[SmallestUnit]] (a Temporal unit), [[LargestUnit]] (a Temporal unit), [[RoundingMode]] (a rounding mode), and [[RoundingIncrement]] (an integer in the inclusive interval from 1 to 109), or a throw completion

description
It reads unit and rounding options needed by difference operations.
1. NOTE: The following steps read options and perform independent validation in alphabetical order. 1. Let _largestUnit_ be ? GetTemporalUnitValuedOption(_options_, *"largestUnit"*, _unitGroup_, ~auto~). 1. If _disallowedUnits_ contains _largestUnit_, throw a *RangeError* exception. 1. Let _roundingIncrement_ be ? GetRoundingIncrementOption(_options_). 1. Let _roundingMode_ be ? GetRoundingModeOption(_options_, ~trunc~). 1. If _operation_ is ~since~, then 1. Set _roundingMode_ to NegateRoundingMode(_roundingMode_). 1. Let _smallestUnit_ be ? GetTemporalUnitValuedOption(_options_, *"smallestUnit"*, _unitGroup_, _fallbackSmallestUnit_). 1. If _disallowedUnits_ contains _smallestUnit_, throw a *RangeError* exception. 1. Let _defaultLargestUnit_ be LargerOfTwoTemporalUnits(_smallestLargestDefaultUnit_, _smallestUnit_). 1. If _largestUnit_ is ~auto~, set _largestUnit_ to _defaultLargestUnit_. 1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception. 1. Let _maximum_ be MaximumTemporalDurationRoundingIncrement(_smallestUnit_). 1. If _maximum_ is not ~unset~, perform ? ValidateTemporalRoundingIncrement(_roundingIncrement_, _maximum_, *false*). 1. Return the Record { [[SmallestUnit]]: _smallestUnit_, [[LargestUnit]]: _largestUnit_, [[RoundingMode]]: _roundingMode_, [[RoundingIncrement]]: _roundingIncrement_, }.