From d9dfd09bfca94b2bdf9cfbe26570ac387db99b9e Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 16 Feb 2026 12:31:54 -0800 Subject: [PATCH 01/17] Normative: Temporal stage 4 --- spec/datetimeformat.html | 546 ++++++++++++++++++++++----- spec/durationformat.html | 225 +---------- spec/locale-sensitive-functions.html | 134 +++++++ spec/locales-currencies-tz.html | 17 - spec/negotiation.html | 29 -- spec/numberformat.html | 250 +----------- 6 files changed, 610 insertions(+), 591 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index ce9f82ca..0388655e 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -53,14 +53,17 @@

_options_: an ECMAScript language value, _required_: ~date~, ~time~, or ~any~, _defaults_: ~date~, ~time~, or ~all~, + optional _toLocaleStringTimeZone_: a primary time zone identifier, ): either a normal completion containing a DateTimeFormat object or a throw completion

+
description
+
If the additional _toLocaleStringTimeZone_ argument is provided, the time zone will be overridden and some adjustments will be made to the defaults in order to implement the behaviour of `Temporal.ZonedDateTime.prototype.toLocaleString`.
- 1. Let _dateTimeFormat_ be ? OrdinaryCreateFromConstructor(_newTarget_, *"%Intl.DateTimeFormat.prototype%"*, « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[DateTimeFormat]], [[BoundFormat]] »). + 1. Let _dateTimeFormat_ be ? OrdinaryCreateFromConstructor(_newTarget_, *"%Intl.DateTimeFormat.prototype%"*, « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[HourCycle]], [[DateStyle]], [[TimeStyle]], [[DateTimeFormat]], [[TemporalPlainDateFormat]], [[TemporalPlainYearMonthFormat]], [[TemporalPlainMonthDayFormat]], [[TemporalPlainTimeFormat]], [[TemporalPlainDateTimeFormat]], [[TemporalInstantFormat]], [[BoundFormat]] »). 1. Let _hour12_ be *undefined*. 1. Let _modifyResolutionOptions_ be a new Abstract Closure with parameters (_options_) that captures _hour12_ and performs the following steps when called: 1. Set _hour12_ to _options_.[[hour12]]. @@ -82,23 +85,26 @@

1. Assert: _hour12_ is *undefined*. 1. Let _hc_ be _r_.[[hc]]. 1. If _hc_ is *null*, set _hc_ to _resolvedLocaleData_.[[hourCycle]]. + 1. Set _dateTimeFormat_.[[HourCycle]] to _hc_. 1. Let _timeZone_ be ? Get(_options_, *"timeZone"*). 1. If _timeZone_ is *undefined*, then - 1. Set _timeZone_ to SystemTimeZoneIdentifier(). + 1. If _toLocaleStringTimeZone_ is present, then + 1. Set _timeZone_ to _toLocaleStringTimeZone_. + 1. Else, + 1. Set _timeZone_ to SystemTimeZoneIdentifier(). 1. Else, + 1. If _toLocaleStringTimeZone_ is present, throw a *TypeError* exception. 1. Set _timeZone_ to ? ToString(_timeZone_). - 1. If IsTimeZoneOffsetString(_timeZone_) is *true*, then - 1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZone_), |UTCOffset|). + 1. If IsOffsetTimeZoneIdentifier(_timeZone_) is *true*, then + 1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZone_), |UTCOffset[~SubMinutePrecision]|). 1. Assert: _parseResult_ is a Parse Node. - 1. If _parseResult_ contains more than one |MinuteSecond| Parse Node, throw a *RangeError* exception. - 1. Let _offsetNanoseconds_ be ParseTimeZoneOffsetString(_timeZone_). + 1. Let _offsetNanoseconds_ be ? ParseDateTimeUTCOffset(_timeZone_). 1. Let _offsetMinutes_ be _offsetNanoseconds_ / (6 × 1010). - 1. Assert: _offsetMinutes_ is an integer. 1. Set _timeZone_ to FormatOffsetTimeZoneIdentifier(_offsetMinutes_). 1. Else, 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_timeZone_). 1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception. - 1. Set _timeZone_ to _timeZoneIdentifierRecord_.[[PrimaryIdentifier]]. + 1. Set _timeZone_ to _timeZoneIdentifierRecord_.[[Identifier]]. 1. Set _dateTimeFormat_.[[TimeZone]] to _timeZone_. 1. Let _formatOptions_ be a new Record. 1. Set _formatOptions_.[[hourCycle]] to _hc_. @@ -118,6 +124,7 @@

1. Set _dateTimeFormat_.[[DateStyle]] to _dateStyle_. 1. Let _timeStyle_ be ? GetOption(_options_, *"timeStyle"*, ~string~, « *"full"*, *"long"*, *"medium"*, *"short"* », *undefined*). 1. Set _dateTimeFormat_.[[TimeStyle]] to _timeStyle_. + 1. Let _formats_ be _resolvedLocaleData_.[[formats]].[[<_resolvedCalendar_>]]. 1. If _dateStyle_ is not *undefined* or _timeStyle_ is not *undefined*, then 1. If _hasExplicitFormatComponents_ is *true*, then 1. Throw a *TypeError* exception. @@ -127,54 +134,35 @@

1. Throw a *TypeError* exception. 1. Let _styles_ be _resolvedLocaleData_.[[styles]].[[<_resolvedCalendar_>]]. 1. Let _bestFormat_ be DateTimeStyleFormat(_dateStyle_, _timeStyle_, _styles_). + 1. If _dateStyle_ is not *undefined*, then + 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _formatMatcher_, « *"weekday"*, *"era"*, *"year"*, *"month"*, *"day"* »). + 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _formatMatcher_, « *"era"*, *"year"*, *"month"* »). + 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _formatMatcher_, « *"month"*, *"day"* »). + 1. Else, + 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to *null*. + 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to *null*. + 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to *null*. + 1. If _timeStyle_ is not *undefined*, then + 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _formatMatcher_, « *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* »). + 1. Else, + 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to *null*. + 1. Set _dateTimeFormat_.[[TemporalPlainDateTimeFormat]] to AdjustDateTimeStyleFormat(_formats_, _bestFormat_, _formatMatcher_, « *"weekday"*, *"era"*, *"year"*, *"month"*, *"day"*, *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* »). + 1. Set _dateTimeFormat_.[[TemporalInstantFormat]] to _bestFormat_. 1. Else, - 1. Let _needDefaults_ be *true*. - 1. If _required_ is ~date~ or ~any~, then - 1. For each property name _prop_ of « *"weekday"*, *"year"*, *"month"*, *"day"* », do - 1. Let _value_ be _formatOptions_.[[<_prop_>]]. - 1. If _value_ is not *undefined*, set _needDefaults_ to *false*. - 1. If _required_ is ~time~ or ~any~, then - 1. For each property name _prop_ of « *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* », do - 1. Let _value_ be _formatOptions_.[[<_prop_>]]. - 1. If _value_ is not *undefined*, set _needDefaults_ to *false*. - 1. If _needDefaults_ is *true* and _defaults_ is either ~date~ or ~all~, then - 1. For each property name _prop_ of « *"year"*, *"month"*, *"day"* », do - 1. Set _formatOptions_.[[<_prop_>]] to *"numeric"*. - 1. If _needDefaults_ is *true* and _defaults_ is either ~time~ or ~all~, then - 1. For each property name _prop_ of « *"hour"*, *"minute"*, *"second"* », do - 1. Set _formatOptions_.[[<_prop_>]] to *"numeric"*. - 1. Let _formats_ be _resolvedLocaleData_.[[formats]].[[<_resolvedCalendar_>]]. - 1. If _formatMatcher_ is *"basic"*, then - 1. Let _bestFormat_ be BasicFormatMatcher(_formatOptions_, _formats_). + 1. Let _bestFormat_ be GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, _required_, _defaults_, ~all~). + 1. Set _dateTimeFormat_.[[TemporalPlainDateFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~date~, ~date~, ~relevant~). + 1. Set _dateTimeFormat_.[[TemporalPlainYearMonthFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~year-month~, ~year-month~, ~relevant~). + 1. Set _dateTimeFormat_.[[TemporalPlainMonthDayFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~month-day~, ~month-day~, ~relevant~). + 1. Set _dateTimeFormat_.[[TemporalPlainTimeFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~time~, ~time~, ~relevant~). + 1. Set _dateTimeFormat_.[[TemporalPlainDateTimeFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~any~, ~all~, ~relevant~). + 1. If _toLocaleStringTimeZone_ is present, then + 1. Set _dateTimeFormat_.[[TemporalInstantFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~any~, ~zoned-date-time~, ~all~). 1. Else, - 1. Let _bestFormat_ be BestFitFormatMatcher(_formatOptions_, _formats_). + 1. Set _dateTimeFormat_.[[TemporalInstantFormat]] to GetDateTimeFormat(_formats_, _formatMatcher_, _formatOptions_, ~any~, ~all~, ~all~). 1. Set _dateTimeFormat_.[[DateTimeFormat]] to _bestFormat_. - 1. If _bestFormat_ has a field [[hour]], then - 1. Set _dateTimeFormat_.[[HourCycle]] to _hc_. 1. Return _dateTimeFormat_. - - -

- FormatOffsetTimeZoneIdentifier ( - _offsetMinutes_: an integer, - ): a String -

-
-
description
-
- It formats a UTC offset, in minutes, into a UTC offset string formatted like ±HH:MM. -
-
- - 1. If _offsetMinutes_ ≥ 0, let _sign_ be the code unit 0x002B (PLUS SIGN); otherwise, let _sign_ be the code unit 0x002D (HYPHEN-MINUS). - 1. Let _absoluteMinutes_ be abs(_offsetMinutes_). - 1. Let _hours_ be floor(_absoluteMinutes_ / 60). - 1. Let _minutes_ be _absoluteMinutes_ modulo 60. - 1. Return the string-concatenation of _sign_, ToZeroPaddedDecimalString(_hours_, 2), the code unit 0x003A (COLON), and ToZeroPaddedDecimalString(_minutes_, 2). - -
@@ -1056,8 +1044,8 @@

Intl.DateTimeFormat.prototype.formatRange ( _startDate_, _endDate_ )

1. Let _dtf_ be *this* value. 1. Perform ? RequireInternalSlot(_dtf_, [[InitializedDateTimeFormat]]). 1. If _startDate_ is *undefined* or _endDate_ is *undefined*, throw a *TypeError* exception. - 1. Let _x_ be ? ToNumber(_startDate_). - 1. Let _y_ be ? ToNumber(_endDate_). + 1. Let _x_ be ? ToDateTimeFormattable(_startDate_). + 1. Let _y_ be ? ToDateTimeFormattable(_endDate_). 1. Return ? FormatDateTimeRange(_dtf_, _x_, _y_).
@@ -1071,8 +1059,8 @@

Intl.DateTimeFormat.prototype.formatRangeToParts ( _startDate_, _endDate_ )< 1. Let _dtf_ be *this* value. 1. Perform ? RequireInternalSlot(_dtf_, [[InitializedDateTimeFormat]]). 1. If _startDate_ is *undefined* or _endDate_ is *undefined*, throw a *TypeError* exception. - 1. Let _x_ be ? ToNumber(_startDate_). - 1. Let _y_ be ? ToNumber(_endDate_). + 1. Let _x_ be ? ToDateTimeFormattable(_startDate_). + 1. Let _y_ be ? ToDateTimeFormattable(_endDate_). 1. Return ? FormatDateTimeRangeToParts(_dtf_, _x_, _y_). @@ -1088,7 +1076,7 @@

Intl.DateTimeFormat.prototype.formatToParts ( _date_ )

1. If _date_ is *undefined*, then 1. Let _x_ be ! Call(%Date.now%, *undefined*). 1. Else, - 1. Let _x_ be ? ToNumber(_date_). + 1. Let _x_ be ? ToDateTimeFormattable(_date_). 1. Return ? FormatDateTimeToParts(_dtf_, _x_).
@@ -1115,10 +1103,16 @@

Properties of Intl.DateTimeFormat Instances

  • [[Locale]] is a String value with the language tag of the locale whose localization is used for formatting.
  • [[Calendar]] is a String value representing the Unicode Calendar Identifier used for formatting.
  • [[NumberingSystem]] is a String value representing the Unicode Number System Identifier used for formatting.
  • -
  • [[TimeZone]] is a String value used for formatting that is either an available named time zone identifier or an offset time zone identifier.
  • -
  • [[HourCycle]] is a String value indicating whether the 12-hour format (*"h11"*, *"h12"*) or the 24-hour format (*"h23"*, *"h24"*) should be used. *"h11"* and *"h23"* start with hour 0 and go up to 11 and 23 respectively. *"h12"* and *"h24"* start with hour 1 and go up to 12 and 24. [[HourCycle]] is only used when [[DateTimeFormat]] has an [[hour]] field.
  • +
  • [[TimeZone]] is an available time zone identifier used for formatting.
  • +
  • [[HourCycle]] is a String value indicating whether the 12-hour format (*"h11"*, *"h12"*) or the 24-hour format (*"h23"*, *"h24"*) should be used. *"h11"* and *"h23"* start with hour 0 and go up to 11 and 23 respectively. *"h12"* and *"h24"* start with hour 1 and go up to 12 and 24.
  • [[DateStyle]], [[TimeStyle]] are each either *undefined*, or a String value with values *"full"*, *"long"*, *"medium"*, or *"short"*.
  • [[DateTimeFormat]] is a DateTime Format Record.
  • +
  • [[TemporalPlainDateFormat]] is either a DateTime Format Record or *null*.
  • +
  • [[TemporalPlainYearMonthFormat]] is either a DateTime Format Record or *null*.
  • +
  • [[TemporalPlainMonthDayFormat]] is either a DateTime Format Record or *null*.
  • +
  • [[TemporalPlainTimeFormat]] is either a DateTime Format Record or *null*.
  • +
  • [[TemporalPlainDateTimeFormat]] is a DateTime Format Record.
  • +
  • [[TemporalInstantFormat]] is a DateTime Format Record.
  • Finally, Intl.DateTimeFormat instances have a [[BoundFormat]] internal slot that caches the function returned by the format accessor ().

    @@ -1197,6 +1191,73 @@

    Abstract Operations for DateTimeFormat Objects

    + +

    + GetDateTimeFormat ( + _formats_: a List of DateTime Format Records, + _matcher_: either *"basic"* or *"best fit"*, + _options_: a Record, + _required_: one of ~date~, ~time~, ~year-month~, ~month-day~, or ~any~, + _defaults_: one of ~date~, ~time~, ~year-month~, ~month-day~, ~zoned-date-time~, or ~all~, + _inherit_: either ~all~ or ~relevant~, + ): either a DateTime Format Record or *null* +

    +
    +
    + + 1. If _required_ is ~date~, then + 1. Let _requiredOptions_ be « *"weekday"*, *"year"*, *"month"*, *"day"* ». + 1. Else if _required_ is ~time~, then + 1. Let _requiredOptions_ be « *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* ». + 1. Else if _required_ is ~year-month~, then + 1. Let _requiredOptions_ be « *"year"*, *"month"* ». + 1. Else if _required_ is ~month-day~, then + 1. Let _requiredOptions_ be « *"month"*, *"day"* ». + 1. Else, + 1. Assert: _required_ is ~any~. + 1. Let _requiredOptions_ be « *"weekday"*, *"year"*, *"month"*, *"day"*, *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* ». + 1. If _defaults_ is ~date~, then + 1. Let _defaultOptions_ be « *"year"*, *"month"*, *"day"* ». + 1. Else if _defaults_ is ~time~, then + 1. Let _defaultOptions_ be « *"hour"*, *"minute"*, *"second"* ». + 1. Else if _defaults_ is ~year-month~, then + 1. Let _defaultOptions_ be « *"year"*, *"month"* ». + 1. Else if _defaults_ is ~month-day~, then + 1. Let _defaultOptions_ be « *"month"*, *"day"* ». + 1. Else, + 1. Assert: _defaults_ is ~zoned-date-time~ or ~all~. + 1. Let _defaultOptions_ be « *"year"*, *"month"*, *"day"*, *"hour"*, *"minute"*, *"second"* ». + 1. If _inherit_ is ~all~, then + 1. Let _formatOptions_ be a copy of _options_. + 1. Else, + 1. Let _formatOptions_ be a new Record. + 1. If _required_ is one of ~date~, ~year-month~, or ~any~, then + 1. Set _formatOptions_.[[era]] to _options_.[[era]]. + 1. If _required_ is either ~time~ or ~any~, then + 1. Set _formatOptions_.[[hourCycle]] to _options_.[[hourCycle]]. + 1. Let _anyPresent_ be *false*. + 1. For each property name _prop_ of « *"weekday"*, *"year"*, *"month"*, *"day"*, *"dayPeriod"*, *"hour"*, *"minute"*, *"second"*, *"fractionalSecondDigits"* », do + 1. If _options_.[[<_prop_>]] is not *undefined*, set _anyPresent_ to *true*. + 1. Let _needDefaults_ be *true*. + 1. For each property name _prop_ of _requiredOptions_, do + 1. Let _value_ be _options_.[[<_prop_>]]. + 1. If _value_ is not *undefined*, then + 1. Set _formatOptions_.[[<_prop_>]] to _value_. + 1. Set _needDefaults_ to *false*. + 1. If _needDefaults_ is *true*, then + 1. If _anyPresent_ is *true* and _inherit_ is ~relevant~, return *null*. + 1. For each property name _prop_ of _defaultOptions_, do + 1. Set _formatOptions_.[[<_prop_>]] to *"numeric"*. + 1. If _defaults_ is ~zoned-date-time~ and _formatOptions_.[[timeZoneName]] is *undefined*, then + 1. Set _formatOptions_.[[timeZoneName]] to *"short"*. + 1. If _matcher_ is *"basic"*, then + 1. Let _bestFormat_ be BasicFormatMatcher(_formatOptions_, _formats_). + 1. Else, + 1. Let _bestFormat_ be BestFitFormatMatcher(_formatOptions_, _formats_). + 1. Return _bestFormat_. + +
    +

    DateTimeStyleFormat ( @@ -1239,6 +1300,41 @@

    + +

    + AdjustDateTimeStyleFormat ( + _formats_: a List of DateTime Format Records, + _baseFormat_: a DateTime Format Record, + _matcher_: either *"basic"* or *"best fit"*, + _allowedOptions_: a List of property names from the "Property" column of , + ): a DateTime Format Record +

    +
    +
    description
    +
    + It inspects _baseFormat_ and determines the closest format to it that only includes fields from _allowedOptions_. + This is used for determining the best format for Temporal objects with the `"dateStyle"` or `"timeStyle"` options. + For example, a locale's best format for `"dateStyle": "full"` might include the weekday, which is not applicable when formatting a `Temporal.PlainYearMonth` object. +
    +
    + + 1. Let _anyConflictingFields_ be *false*. + 1. For each row of , except the header row, in table order, do + 1. Let _prop_ be the name given in the "Property" column of the current row. + 1. If _baseFormat_ has a [[<_prop_>]] field and _allowedOptions_ does not contain _prop_, set _anyConflictingFields_ to true. + 1. If _anyConflictingFields_ is *false*, return _baseFormat_. + 1. NOTE: The above steps prevent the operation from returning an altered format when _baseFormat_ would be sufficient. This should be unnecessary, but exists because the ECMA-402 specification does not guarantee that a format returned from DateTimeStyleFormat can also be returned from BasicFormatMatcher or BestFitFormatMatcher. + 1. Let _formatOptions_ be a new Record. + 1. For each property name _prop_ of _allowedOptions_, do + 1. If _baseFormat_ has a [[<_prop_>]] field, set _formatOptions_.[[<_prop_>]] to _baseFormat_.[[<_prop_>]]. + 1. If _matcher_ is *"basic"*, then + 1. Let _bestFormat_ be BasicFormatMatcher(_formatOptions_, _formats_). + 1. Else, + 1. Let _bestFormat_ be BestFitFormatMatcher(_formatOptions_, _formats_). + 1. Return _bestFormat_. + +
    +

    BasicFormatMatcher ( @@ -1331,7 +1427,7 @@

    DateTime Format Functions

    1. If _date_ is not provided or is *undefined*, then 1. Let _x_ be ! Call(%Date.now%, *undefined*). 1. Else, - 1. Let _x_ be ? ToNumber(_date_). + 1. Let _x_ be ? ToDateTimeFormattable(_date_). 1. Return ? FormatDateTime(_dtf_, _x_). @@ -1345,11 +1441,12 @@

    _format_: a DateTime Format Record or a DateTime Range Pattern Format Record, _pattern_: a Pattern String, _epochNanoseconds_: a BigInt, + _isPlain_: a Boolean, ): a List of Records with fields [[Type]] (a String) and [[Value]] (a String)

    description
    -
    It creates the corresponding parts for the epoch time _epochNanoseconds_ according to _pattern_ and to the effective locale and the formatting options of _dateTimeFormat_ and _format_.
    +
    It creates the corresponding parts for the epoch time _epochNanoseconds_ according to _pattern_ and to the effective locale and the formatting options of _dateTimeFormat_ and _format_, with _isPlain_ signifying whether _epochNanoseconds_ is an epoch-time representation of a wall-clock time.
    1. Let _locale_ be _dateTimeFormat_.[[Locale]]. @@ -1369,7 +1466,8 @@

    1. Perform ! CreateDataPropertyOrThrow(_nf3Options_, *"numberingSystem"*, _dateTimeFormat_.[[NumberingSystem]]). 1. Perform ! CreateDataPropertyOrThrow(_nf3Options_, *"useGrouping"*, *false*). 1. Let _nf3_ be ! Construct(%Intl.NumberFormat%, « _locale_, _nf3Options_ »). - 1. Let _tm_ be ToLocalTime(_epochNanoseconds_, _dateTimeFormat_.[[Calendar]], _dateTimeFormat_.[[TimeZone]]). + 1. If _isPlain_ is *true*, let _timeZone_ be *"+00:00"*; else let _timeZone_ be _dateTimeFormat_.[[TimeZone]]. + 1. Let _tm_ be ToLocalTime(_epochNanoseconds_, _dateTimeFormat_.[[Calendar]], _timeZone_). 1. Let _patternParts_ be PartitionPattern(_pattern_). 1. Let _result_ be a new empty List. 1. For each Record { [[Type]], [[Value]] } _patternPart_ of _patternParts_, do @@ -1447,23 +1545,22 @@

    PartitionDateTimePattern ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, + _x_: either a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing a List of Records with fields [[Type]] (a String) and [[Value]] (a String), or a throw completion

    description
    -
    It interprets _x_ as a time value as specified in ECMA-262, , and creates the corresponding parts according to the effective locale and the formatting options of _dateTimeFormat_.
    +
    It interprets _x_ as an epoch time, and creates the corresponding parts according to the type of _x_, effective locale, and the formatting options of _dateTimeFormat_.
    - 1. Let _x_ be TimeClip(_x_). - 1. If _x_ is *NaN*, throw a *RangeError* exception. - 1. Let _epochNanoseconds_ be ℤ(ℝ(_x_) × 106). - 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. - 1. If _dateTimeFormat_.[[HourCycle]] is *"h11"* or *"h12"*, then + 1. Let _formatRecord_ be ? ValueFormatRecord(_dateTimeFormat_, _x_). + 1. Let _epochNanoseconds_ be _formatRecord_.[[EpochNanoseconds]]. + 1. Let _format_ be _formatRecord_.[[Format]]. + 1. If _format_ has a field [[hour]] and _dateTimeFormat_.[[HourCycle]] is *"h11"* or *"h12"*, then 1. Let _pattern_ be _format_.[[pattern12]]. 1. Else, 1. Let _pattern_ be _format_.[[pattern]]. - 1. Let _result_ be FormatDateTimePattern(_dateTimeFormat_, _format_, _pattern_, _epochNanoseconds_). + 1. Let _result_ be FormatDateTimePattern(_dateTimeFormat_, _format_, _pattern_, _epochNanoseconds_, _formatRecord_.[[IsPlain]]). 1. Return _result_.
    @@ -1472,7 +1569,7 @@

    FormatDateTime ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, + _x_: either a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing a String or a throw completion

    @@ -1490,7 +1587,7 @@

    FormatDateTimeToParts ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, + _x_: a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing an Array or a throw completion

    @@ -1513,25 +1610,27 @@

    PartitionDateTimeRangePattern ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, - _y_: a Number, + _x_: either a Number or an Object for which IsTemporalObject returns *true*, + _y_: either a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing a List of Records with fields [[Type]] (a String), [[Value]] (a String), and [[Source]] (a String), or a throw completion

    description
    -
    It interprets _x_ and _y_ as time values as specified in ECMA-262, , and creates the corresponding parts according to the effective locale and the formatting options of _dateTimeFormat_.
    +
    It interprets _x_ and _y_ as epoch times, and creates the corresponding parts according to the types of _x_ and _y_, effective locale, and the formatting options of _dateTimeFormat_.
    - 1. Set _x_ to TimeClip(_x_). - 1. If _x_ is *NaN*, throw a *RangeError* exception. - 1. Set _y_ to TimeClip(_y_). - 1. If _y_ is *NaN*, throw a *RangeError* exception. - 1. Let _xEpochNanoseconds_ be ℤ(ℝ(_x_) × 106). - 1. Let _yEpochNanoseconds_ be ℤ(ℝ(_y_) × 106). + 1. If IsTemporalObject(_x_) is *true* or IsTemporalObject(_y_) is *true*, then + 1. If SameTemporalType(_x_, _y_) is *false*, throw a *TypeError* exception. + 1. Let _xFormatRecord_ be ? ValueFormatRecord(_dateTimeFormat_, _x_). + 1. Let _yFormatRecord_ be ? ValueFormatRecord(_dateTimeFormat_, _y_). + 1. Let _xEpochNanoseconds_ be _xFormatRecord_.[[EpochNanoseconds]]. + 1. Let _yEpochNanoseconds_ be _yFormatRecord_.[[EpochNanoseconds]]. 1. Let _tm1_ be ToLocalTime(_xEpochNanoseconds_, _dateTimeFormat_.[[Calendar]], _dateTimeFormat_.[[TimeZone]]). 1. Let _tm2_ be ToLocalTime(_yEpochNanoseconds_, _dateTimeFormat_.[[Calendar]], _dateTimeFormat_.[[TimeZone]]). - 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. - 1. If _dateTimeFormat_.[[HourCycle]] is *"h11"* or *"h12"*, then + 1. Let _format_ be _xFormatRecord_.[[Format]]. + 1. Assert: _format_ is equal to _yFormatRecord_.[[Format]]. + 1. Assert: _xFormatRecord_.[[IsPlain]] = _yFormatRecord_.[[IsPlain]]. + 1. If _format_ has a field [[hour]] and _dateTimeFormat_.[[HourCycle]] is *"h11"* or *"h12"*, then 1. Let _pattern_ be _format_.[[pattern12]]. 1. Let _rangePatterns_ be _format_.[[rangePatterns12]]. 1. Else, @@ -1569,7 +1668,7 @@

    1. Set _relevantFieldsEqual_ to *false*. 1. If _relevantFieldsEqual_ is *true*, then 1. Let _collapsedResult_ be a new empty List. - 1. Let _resultParts_ be FormatDateTimePattern(_dateTimeFormat_, _format_, _pattern_, _xEpochNanoseconds_). + 1. Let _resultParts_ be FormatDateTimePattern(_dateTimeFormat_, _format_, _pattern_, _xEpochNanoseconds_, _xFormatRecord_.[[IsPlain]]). 1. For each Record { [[Type]], [[Value]] } _r_ of _resultParts_, do 1. Append the Record { [[Type]]: _r_.[[Type]], [[Value]]: _r_.[[Value]], [[Source]]: *"shared"* } to _collapsedResult_. 1. Return _collapsedResult_. @@ -1583,7 +1682,7 @@

    1. Let _z_ be _xEpochNanoseconds_. 1. Else, 1. Let _z_ be _yEpochNanoseconds_. - 1. Let _resultParts_ be FormatDateTimePattern(_dateTimeFormat_, _selectedRangePattern_, _pattern_, _z_). + 1. Let _resultParts_ be FormatDateTimePattern(_dateTimeFormat_, _selectedRangePattern_, _pattern_, _z_, _xFormatRecord_.[[IsPlain]]). 1. For each Record { [[Type]], [[Value]] } _r_ of _resultParts_, do 1. Append the Record { [[Type]]: _r_.[[Type]], [[Value]]: _r_.[[Value]], [[Source]]: _source_ } to _rangeResult_. 1. Return _rangeResult_. @@ -1594,8 +1693,8 @@

    FormatDateTimeRange ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, - _y_: a Number, + _x_: either a Number or an Object for which IsTemporalObject returns *true*, + _y_: either a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing a String or a throw completion

    @@ -1613,8 +1712,8 @@

    FormatDateTimeRangeToParts ( _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, - _y_: a Number, + _x_: either a Number or an Object for which IsTemporalObject returns *true*, + _y_: either a Number or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing an Array or a throw completion

    @@ -1634,6 +1733,267 @@

    + +

    + ToDateTimeFormattable ( + _value_: an ECMAScript language value, but not *undefined*, + ): either a normal completion containing either a Number or an Object for which IsTemporalObject returns *true*, or a throw completion +

    +
    +
    description
    +
    It converts _value_ to a value that can be formatted by an %Intl.DateTimeFormat% object.
    +
    + + 1. If IsTemporalObject(_value_) is *true*, return _value_. + 1. Return ? ToNumber(_value_). + +
    + + +

    + IsTemporalObject ( + _value_: an ECMAScript language value, + ): a Boolean +

    +
    +
    + + 1. If _value_ is not an Object, return *false*. + 1. If _value_ does not have an [[InitializedTemporalDate]], [[InitializedTemporalTime]], [[InitializedTemporalDateTime]], [[InitializedTemporalZonedDateTime]], [[InitializedTemporalYearMonth]], [[InitializedTemporalMonthDay]], or [[InitializedTemporalInstant]] internal slot, return *false*. + 1. Return *true*. + +
    + + +

    + SameTemporalType ( + _x_: an ECMAScript language value, + _y_: an ECMAScript language value, + ): a Boolean +

    +
    +
    description
    +
    It determines whether _x_ and _y_ are both instances of the same Temporal type.
    +
    + + 1. If either of IsTemporalObject(_x_) or IsTemporalObject(_y_) is *false*, return *false*. + 1. If _x_ has an [[InitializedTemporalDate]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalTime]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalDateTime]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalZonedDateTime]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalYearMonth]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalMonthDay]] internal slot and _y_ does not, return *false*. + 1. If _x_ has an [[InitializedTemporalInstant]] internal slot and _y_ does not, return *false*. + 1. Return *true*. + +
    + + +

    Value Format Records

    + +

    Each Value Format Record has the fields defined in .

    + + + Record returned by ValueFormatRecord + + + + + + + + + + + + + + + + + + + +
    Field NameValue Type
    [[Format]]a DateTime Format Record
    [[EpochNanoseconds]]a BigInt
    [[IsPlain]]a Boolean
    +
    +
    + + +

    + PlainDateFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _temporalDate_: a Temporal.PlainDate, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. If _temporalDate_.[[Calendar]] is not either _dateTimeFormat_.[[Calendar]] or *"iso8601"*, throw a *RangeError* exception. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalDate_.[[ISODate]], NoonTimeRecord()). + 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateFormat]]. + 1. If _format_ is *null*, throw a *TypeError* exception. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNs_, + [[IsPlain]]: *true* + }. + +
    + + +

    + PlainYearMonthFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _temporalYearMonth_: a Temporal.PlainYearMonth, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. If _temporalYearMonth_.[[Calendar]] is not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalYearMonth_.[[ISODate]], NoonTimeRecord()). + 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainYearMonthFormat]]. + 1. If _format_ is *null*, throw a *TypeError* exception. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNs_, + [[IsPlain]]: *true* + }. + +
    + + +

    + PlainMonthDayFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _temporalMonthDay_: a Temporal.PlainMonthDay, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. If _temporalMonthDay_.[[Calendar]] is not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalMonthDay_.[[ISODate]], NoonTimeRecord()). + 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainMonthDayFormat]]. + 1. If _format_ is *null*, throw a *TypeError* exception. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNs_, + [[IsPlain]]: *true* + }. + +
    + + +

    + PlainTimeFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _temporalTime_: a Temporal.PlainTime, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. Let _isoDate_ be CreateISODateRecord(1970, 1, 1). + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_isoDate_, _temporalTime_.[[Time]]). + 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainTimeFormat]]. + 1. If _format_ is *null*, throw a *TypeError* exception. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNs_, + [[IsPlain]]: *true* + }. + +
    + + +

    + PlainDateTimeFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _dateTime_: a Temporal.PlainDateTime, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. If _dateTime_.[[Calendar]] is not *"iso8601"* and not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. + 1. Let _epochNs_ be GetUTCEpochNanoseconds(_dateTime_.[[ISODateTime]]). + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateTimeFormat]]. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNs_, + [[IsPlain]]: *true* + }. + +
    + + +

    + InstantFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _instant_: a Temporal.Instant, + ): a Value Format Record +

    +
    +
    + + 1. Let _format_ be _dateTimeFormat_.[[TemporalInstantFormat]]. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _instant_.[[EpochNanoseconds]], + [[IsPlain]]: *false* + }. + +
    + + +

    + TimeValueFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _x_: a Number, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. Set _x_ to TimeClip(_x_). + 1. If _x_ is *NaN*, throw a *RangeError* exception. + 1. Let _epochNanoseconds_ be ℤ(ℝ(_x_) × 106). + 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. + 1. Return Value Format Record { + [[Format]]: _format_, + [[EpochNanoseconds]]: _epochNanoseconds_, + [[IsPlain]]: *false* + }. + +
    + + +

    + ValueFormatRecord ( + _dateTimeFormat_: an Intl.DateTimeFormat, + _x_: either a Number, or an Object for which IsTemporalObject returns *true*, + ): either a normal completion containing a Value Format Record or a throw completion +

    +
    +
    + + 1. If _x_ is a Number, return ? TimeValueFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalDate]] internal slot, return ? PlainDateFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalYearMonth]] internal slot, return ? PlainYearMonthFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalMonthDay]] internal slot, return ? PlainMonthDayFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalTime]] internal slot, return ? PlainTimeFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalDateTime]] internal slot, return ? PlainDateTimeFormatRecord(_dateTimeFormat_, _x_). + 1. If _x_ has an [[InitializedTemporalInstant]] internal slot, return InstantFormatRecord(_dateTimeFormat_, _x_). + 1. Assert: _x_ has an [[InitializedTemporalZonedDateTime]] internal slot. + 1. Throw a *TypeError* exception. + +
    +

    ToLocalTime ( @@ -1649,8 +2009,8 @@

    - 1. If IsTimeZoneOffsetString(_timeZoneIdentifier_) is *true*, then - 1. Let _offsetNs_ be ParseTimeZoneOffsetString(_timeZoneIdentifier_). + 1. If IsOffsetTimeZoneIdentifier(_timeZoneIdentifier_) is *true*, then + 1. Let _offsetNs_ be ! ParseDateTimeUTCOffset(_timeZoneIdentifier_). 1. Else, 1. Assert: GetAvailableNamedTimeZoneIdentifier(_timeZoneIdentifier_) is not ~empty~. 1. Let _offsetNs_ be GetNamedTimeZoneOffsetNanoseconds(_timeZoneIdentifier_, _epochNs_). @@ -1658,8 +2018,14 @@

    1. If _calendar_ is *"gregory"*, then 1. Return a ToLocalTime Record with fields calculated from _tz_ according to . 1. Else, - 1. Return a ToLocalTime Record with the fields calculated from _tz_ for the given _calendar_. The calculations should use best available information about the specified _calendar_. + 1. Return a ToLocalTime Record with the fields calculated from _tz_ for the given _calendar_. The calculations should use best available information about the specified _calendar_. Given the same values of _epochNs_, _calendar_, and _timeZoneIdentifier_, the result must be the same for the lifetime of the surrounding agent. + + + Time zone information is subject to change, and host environments may update their time zone database at any time. + At a minimum, implementations must ensure that the time zone information for each particular value of _timeZone_ individually remains constant starting from the time it is first accessed, for the lifetime of the surrounding agent. + Furthermore, it is recommended that the time zone information for all values of _timeZone_ as a whole (i.e. the time zone database) remains the same for the lifetime of the surrounding agent. + diff --git a/spec/durationformat.html b/spec/durationformat.html index 0edb055f..59f58d41 100644 --- a/spec/durationformat.html +++ b/spec/durationformat.html @@ -308,15 +308,15 @@

    Intl.DurationFormat.prototype.resolvedOptions ( )

    -

    Intl.DurationFormat.prototype.format ( _duration_ )

    +

    Intl.DurationFormat.prototype.format ( _durationLike_ )

    -

    When the `format` method is called with an argument _duration_, the following steps are taken:

    +

    When the `format` method is called with an argument _durationLike_, the following steps are taken:

    1. Let _df_ be the *this* value. 1. Perform ? RequireInternalSlot(_df_, [[InitializedDurationFormat]]). - 1. Let _record_ be ? ToDurationRecord(_duration_). - 1. Let _parts_ be PartitionDurationFormatPattern(_df_, _record_). + 1. Let _duration_ be ? ToTemporalDuration(_durationLike_). + 1. Let _parts_ be PartitionDurationFormatPattern(_df_, _duration_). 1. Let _result_ be the empty String. 1. For each Record { [[Type]], [[Value]], [[Unit]] } _part_ in _parts_, do 1. Set _result_ to the string-concatenation of _result_ and _part_.[[Value]]. @@ -325,15 +325,15 @@

    Intl.DurationFormat.prototype.format ( _duration_ )

    -

    Intl.DurationFormat.prototype.formatToParts ( _duration_ )

    +

    Intl.DurationFormat.prototype.formatToParts ( _durationLike_ )

    -

    When the `formatToParts` method is called with an argument _duration_, the following steps are taken:

    +

    When the `formatToParts` method is called with an argument _durationLike_, the following steps are taken:

    1. Let _df_ be the *this* value. 1. Perform ? RequireInternalSlot(_df_, [[InitializedDurationFormat]]). - 1. Let _record_ be ? ToDurationRecord(_duration_). - 1. Let _parts_ be PartitionDurationFormatPattern(_df_, _record_). + 1. Let _duration_ be ? ToTemporalDuration(_durationLike_). + 1. Let _parts_ be PartitionDurationFormatPattern(_df_, _duration_). 1. Let _result_ be ! ArrayCreate(0). 1. Let _n_ be 0. 1. For each Record { [[Type]], [[Value]], [[Unit]] } _part_ in _parts_, do @@ -385,197 +385,6 @@

    Properties of Intl.DurationFormat Instances

    Abstract Operations for DurationFormat Objects

    - -

    Duration Records

    -

    A Duration Record is a Record value used to represent a Duration.

    -

    Duration Records have the fields listed in

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FieldMeaning
    [[Years]] - The number of years in the duration. -
    [[Months]] - The number of months in the duration. -
    [[Weeks]] - The number of weeks in the duration. -
    [[Days]] - The number of days in the duration. -
    [[Hours]] - The number of hours in the duration. -
    [[Minutes]] - The number of minutes in the duration. -
    [[Seconds]] - The number of seconds in the duration. -
    [[Milliseconds]] - The number of milliseconds in the duration. -
    [[Microseconds]] - The number of microseconds in the duration. -
    [[Nanoseconds]] - The number of nanoseconds in the duration. -
    -
    -
    - - -

    - 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_). - -
    - - -

    - ToDurationRecord ( - _input_: an ECMAScript language value, - ): either a normal completion containing a Duration Record, or a throw completion -

    -
    -
    description
    -
    It converts a given object that represents a Duration into a Duration Record.
    -
    - - - 1. If _input_ is not an Object, then - 1. If _input_ is a String, throw a *RangeError* exception. - 1. Throw a *TypeError* exception. - 1. Let _result_ be a new Duration Record with each field set to 0. - 1. Let _days_ be ? Get(_input_, *"days"*). - 1. If _days_ is not *undefined*, set _result_.[[Days]] to ? ToIntegerIfIntegral(_days_). - 1. Let _hours_ be ? Get(_input_, *"hours"*). - 1. If _hours_ is not *undefined*, set _result_.[[Hours]] to ? ToIntegerIfIntegral(_hours_). - 1. Let _microseconds_ be ? Get(_input_, *"microseconds"*). - 1. If _microseconds_ is not *undefined*, set _result_.[[Microseconds]] to ? ToIntegerIfIntegral(_microseconds_). - 1. Let _milliseconds_ be ? Get(_input_, *"milliseconds"*). - 1. If _milliseconds_ is not *undefined*, set _result_.[[Milliseconds]] to ? ToIntegerIfIntegral(_milliseconds_). - 1. Let _minutes_ be ? Get(_input_, *"minutes"*). - 1. If _minutes_ is not *undefined*, set _result_.[[Minutes]] to ? ToIntegerIfIntegral(_minutes_). - 1. Let _months_ be ? Get(_input_, *"months"*). - 1. If _months_ is not *undefined*, set _result_.[[Months]] to ? ToIntegerIfIntegral(_months_). - 1. Let _nanoseconds_ be ? Get(_input_, *"nanoseconds"*). - 1. If _nanoseconds_ is not *undefined*, set _result_.[[Nanoseconds]] to ? ToIntegerIfIntegral(_nanoseconds_). - 1. Let _seconds_ be ? Get(_input_, *"seconds"*). - 1. If _seconds_ is not *undefined*, set _result_.[[Seconds]] to ? ToIntegerIfIntegral(_seconds_). - 1. Let _weeks_ be ? Get(_input_, *"weeks"*). - 1. If _weeks_ is not *undefined*, set _result_.[[Weeks]] to ? ToIntegerIfIntegral(_weeks_). - 1. Let _years_ be ? Get(_input_, *"years"*). - 1. If _years_ is not *undefined*, set _result_.[[Years]] to ? ToIntegerIfIntegral(_years_). - 1. If _years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ are all *undefined*, throw a *TypeError* exception. - 1. If IsValidDuration( _result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]) is *false*, then - 1. Throw a *RangeError* exception. - 1. Return _result_. - -
    - - -

    - DurationSign ( - _duration_: a Duration Record, - ): -1, 0, or 1 -

    -
    -
    description
    -
    It returns 1 if the most significant non-zero field in the _duration_ argument is positive, and -1 if the most significant non-zero field is negative. If all of _duration_'s fields are zero, it returns 0.
    -
    - - 1. For each value _v_ of « _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]] », do - 1. If _v_ < 0, return -1. - 1. If _v_ > 0, return 1. - 1. Return 0. - -
    - - -

    - IsValidDuration ( - _years_: an integer, - _months_: an integer, - _weeks_: an integer, - _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, - ): a Boolean -

    -
    -
    description
    -
    It returns *true* if its arguments form valid input from which to construct a Duration Record, and *false* otherwise.
    -
    - - 1. Let _sign_ be 0. - 1. For each value _v_ of « _years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_ », do - 1. If 𝔽(_v_) is not finite, return *false*. - 1. If _v_ < 0, then - 1. If _sign_ > 0, return *false*. - 1. Set _sign_ to -1. - 1. Else if _v_ > 0, then - 1. If _sign_ < 0, return *false*. - 1. Set _sign_ to 1. - 1. If abs(_years_) ≥ 232, return *false*. - 1. If abs(_months_) ≥ 232, return *false*. - 1. If abs(_weeks_) ≥ 232, return *false*. - 1. Let _normalizedSeconds_ be _days_ × 86,400 + _hours_ × 3600 + _minutes_ × 60 + _seconds_ + ℝ(𝔽(_milliseconds_)) × 10-3 + ℝ(𝔽(_microseconds_)) × 10-6 + ℝ(𝔽(_nanoseconds_)) × 10-9. - 1. NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10-3, 10-6, and 10-9 respectively may be imprecise when _milliseconds_, _microseconds_, or _nanoseconds_ is an unsafe integer. This multiplication can be implemented in C++ with an implementation of `std::remquo()` with sufficient bits in the quotient. String manipulation will also give an exact result, since the multiplication is by a power of 10. - 1. If abs(_normalizedSeconds_) ≥ 253, return *false*. - 1. Return *true*. - -
    -

    GetDurationUnitOptions ( @@ -667,7 +476,7 @@

    ComputeFractionalDigits ( _durationFormat_: a DurationFormat Object, - _duration_: a Duration Record, + _duration_: a Temporal.Duration, ): a mathematical value

    @@ -679,11 +488,11 @@

    1. Let _result_ be 0. 1. Let _exponent_ be 3. 1. For each row of , except the header row, in table order, do - 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the Internal Slot value of the current row. + 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the "Formatter Internal Slot" value of the current row. 1. If _unitOptions_.[[Style]] is *"fractional"*, then 1. Let _unit_ be the Unit value of the current row. 1. Assert: IsFractionalSecondUnitName(_unit_) is *true*. - 1. Let _value_ be the value of _duration_'s field whose name is the Value Field value of the current row. + 1. Let _value_ be the value of _duration_'s internal slot whose name is the "Duration Internal Slot" value of the current row. 1. Set _result_ to _result_ + (_value_ / 10_exponent_). 1. Set _exponent_ to _exponent_ + 3. 1. Return _result_. @@ -829,7 +638,7 @@

    FormatNumericUnits ( _durationFormat_: a DurationFormat Object, - _duration_: a Duration Record, + _duration_: a Temporal.Duration, _firstNumericUnit_: a String, _signDisplayed_: a Boolean, ): a List of Records @@ -946,7 +755,7 @@

    PartitionDurationFormatPattern ( _durationFormat_: a DurationFormat, - _duration_: a Duration Record, + _duration_: a Temporal.Duration, ): a List

    @@ -959,8 +768,8 @@

    1. Let _signDisplayed_ be *true*. 1. Let _numericUnitFound_ be *false*. 1. While _numericUnitFound_ is *false*, repeat for each row in in table order, except the header row: - 1. Let _value_ be the value of _duration_'s field whose name is the Value Field value of the current row. - 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the Internal Slot value of the current row. + 1. Let _value_ be the value of _duration_'s internal slot whose name is the "Duration Internal Slot" value of the current row. + 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the "Formatter Internal Slot" value of the current row. 1. Let _style_ be _unitOptions_.[[Style]]. 1. Let _display_ be _unitOptions_.[[Display]]. 1. Let _unit_ be the Unit value of the current row. @@ -1006,8 +815,8 @@

    - - + + diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index e929b0e4..90ebd839 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -247,4 +247,138 @@

    Array.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    + + +

    Properties of the Temporal.Duration Prototype Object

    + + +

    Temporal.Duration.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _duration_ be the *this* value. + 1. Perform ? RequireInternalSlot(_duration_, [[InitializedTemporalDuration]]). + 1. Let _formatter_ be ? Construct(%Intl.DurationFormat%, « _locales_, _options_ »). + 1. Let _parts_ be PartitionDurationFormatPattern(_formatter_, _duration_). + 1. Let _result_ be the empty String. + 1. For each Record { [[Type]], [[Value]], [[Unit]] } _part_ in _parts_, do + 1. Set _result_ to the string-concatenation of _result_ and _part_.[[Value]]. + 1. Return _result_. + +
    +
    + + +

    Properties of the Temporal.Instant Prototype Object

    + + +

    Temporal.Instant.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _instant_ be the *this* value. + 1. Perform ? RequireInternalSlot(_instant_, [[InitializedTemporalInstant]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~any~, ~all~). + 1. Return ? FormatDateTime(_dateFormat_, _instant_). + +
    +
    + + +

    Properties of the Temporal.PlainDate Prototype Object

    + + +

    Temporal.PlainDate.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _plainDate_ be the *this* value. + 1. Perform ? RequireInternalSlot(_plainDate_, [[InitializedTemporalDate]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~date~, ~date~). + 1. Return ? FormatDateTime(_dateFormat_, _plainDate_). + +
    +
    + + +

    Properties of the Temporal.PlainDateTime Prototype Object

    + + +

    Temporal.PlainDateTime.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _plainDateTime_ be the *this* value. + 1. Perform ? RequireInternalSlot(_plainDateTime_, [[InitializedTemporalDateTime]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~any~, ~all~). + 1. Return ? FormatDateTime(_dateFormat_, _plainDateTime_). + +
    +
    + + +

    Properties of the Temporal.PlainMonthDay Prototype Object

    + + +

    Temporal.PlainMonthDay.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _plainMonthDay_ be the *this* value. + 1. Perform ? RequireInternalSlot(_plainMonthDay_, [[InitializedTemporalMonthDay]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~date~, ~date~). + 1. Return ? FormatDateTime(_dateFormat_, _plainMonthDay_). + +
    +
    + + +

    Properties of the Temporal.PlainTime Prototype Object

    + + +

    Temporal.PlainTime.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _plainTime_ be the *this* value. + 1. Perform ? RequireInternalSlot(_plainTime_, [[InitializedTemporalTime]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~time~, ~time~). + 1. Return ? FormatDateTime(_dateFormat_, _plainTime_). + +
    +
    + + +

    Properties of the Temporal.PlainYearMonth Prototype Object

    + + +

    Temporal.PlainYearMonth.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _plainYearMonth_ be the *this* value. + 1. Perform ? RequireInternalSlot(_plainYearMonth_, [[InitializedTemporalYearMonth]]). + 1. Let _dateFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~date~, ~date~). + 1. Return ? FormatDateTime(_dateFormat_, _plainYearMonth_). + +
    +
    + + +

    Properties of the Temporal.ZonedDateTime Prototype Object

    + + +

    Temporal.ZonedDateTime.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )

    +

    This definition supersedes the definition provided in .

    +

    This method performs the following steps when called:

    + + 1. Let _zonedDateTime_ be the *this* value. + 1. Perform ? RequireInternalSlot(_zonedDateTime_, [[InitializedTemporalZonedDateTime]]). + 1. Let _dateTimeFormat_ be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, _locales_, _options_, ~any~, ~all~, _zonedDateTime_.[[TimeZone]]). + 1. If _zonedDateTime_.[[Calendar]] is not *"iso8601"* and CalendarEquals(_zonedDateTime_.[[Calendar]], _dateTimeFormat_.[[Calendar]]) is *false*, throw a *RangeError* exception. + 1. Let _instant_ be ! CreateTemporalInstant(_zonedDateTime_.[[EpochNanoseconds]]). + 1. Return ? FormatDateTime(_dateTimeFormat_, _instant_). + +
    +
    diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index be80bda3..e45af632 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -10,9 +10,6 @@

    Case Sensitivity and Case Mapping

    For example, *"ß"* (U+00DF) must not match or be mapped to *"SS"* (U+0053, U+0053). *"ı"* (U+0131) must not match or be mapped to *"I"* (U+0049). -

    The ASCII-uppercase of a String value _S_ is the String value derived from _S_ by replacing each occurrence of an ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) with the corresponding ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) while preserving all other code units.

    -

    The ASCII-lowercase of a String value _S_ is the String value derived from _S_ by replacing each occurrence of an ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) with the corresponding ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) while preserving all other code units.

    -

    A String value _A_ is an ASCII-case-insensitive match for String value _B_ if the ASCII-uppercase of _A_ is exactly the same sequence of code units as the ASCII-uppercase of _B_. A sequence of Unicode code points _A_ is an ASCII-case-insensitive match for _B_ if _B_ is an ASCII-case-insensitive match for CodePointsToString(_A_).

    @@ -518,20 +515,6 @@

    AvailableCanonicalCollations ( ): a List of Strings

    - -

    Calendar Types

    - -

    This specification identifies calendars using a calendar type as defined by Unicode Technical Standard #35 Part 4 Dates, Section 2 Calendar Elements. Their canonical form is a string containing only Unicode Basic Latin lowercase letters (U+0061 LATIN SMALL LETTER A through U+007A LATIN SMALL LETTER Z) with zero or more medial hyphens (U+002D HYPHEN-MINUS).

    - - -

    AvailableCalendars ( ): a List of Strings

    -
    -
    description
    -
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., either both or neither of *"islamicc"* and *"islamic-civil"*). The List must include *"iso8601"*.
    -
    -
    -
    -

    Pattern String Types

    Pattern String is a String value which contains zero or more substrings of the form *"{key}"*, where key can be any non-empty sequence consisting only of elements from the ASCII word characters. The syntax of the abstract pattern strings is an implementation detail and is not exposed to users of ECMA-402.

    diff --git a/spec/negotiation.html b/spec/negotiation.html index 34f75ffb..edf76dbe 100644 --- a/spec/negotiation.html +++ b/spec/negotiation.html @@ -366,35 +366,6 @@

    - -

    - GetOption ( - _options_: an Object, - _property_: a property key, - _type_: ~boolean~ or ~string~, - _values_: ~empty~ or a List of ECMAScript language values, - _default_: ~required~ or an ECMAScript language value, - ): either a normal completion containing an ECMAScript language value or a throw completion -

    -
    -
    description
    -
    It extracts the value of the specified property of _options_, converts it to the required _type_, checks whether it is allowed by _values_ if _values_ is not ~empty~, and substitutes _default_ if the value is *undefined*.
    -
    - - 1. Let _value_ be ? Get(_options_, _property_). - 1. If _value_ is *undefined*, then - 1. If _default_ is ~required~, throw a *RangeError* exception. - 1. Return _default_. - 1. If _type_ is ~boolean~, then - 1. Set _value_ to ToBoolean(_value_). - 1. Else, - 1. Assert: _type_ is ~string~. - 1. Set _value_ to ? ToString(_value_). - 1. If _values_ is not ~empty~ and _values_ does not contain _value_, throw a *RangeError* exception. - 1. Return _value_. - -
    -

    GetBooleanOrStringNumberFormatOption ( diff --git a/spec/numberformat.html b/spec/numberformat.html index 82ec6bc0..bb518293 100644 --- a/spec/numberformat.html +++ b/spec/numberformat.html @@ -535,112 +535,10 @@

    Properties of Intl.NumberFormat Instances

    In scientific notation, this slot affects the sign display of the mantissa but not the exponent.
  • [[RoundingIncrement]] is an integer that evenly divides 10, 100, 1000, or 10000 into tenths, fifths, quarters, or halves. It indicates the increment at which rounding should take place relative to the calculated rounding magnitude. For example, if [[MaximumFractionDigits]] is 2 and [[RoundingIncrement]] is 5, then formatted numbers are rounded to the nearest 0.05 ("nickel rounding").
  • -
  • [[RoundingMode]] is a rounding mode, one of the String values in the Identifier column of .
  • +
  • [[RoundingMode]] is a rounding mode, one of the Strings in the "String Identifier" column of .
  • [[TrailingZeroDisplay]] is one of the String values *"auto"* or *"stripIfInteger"*, indicating whether to strip trailing zeros if the formatted number is an integer (i.e., has no non-zero fraction digit).
  • - - Rounding modes in Intl.NumberFormat -

    Value FieldInternal SlotDuration Internal SlotFormatter Internal Slot Unit NumberFormat Unit
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IdentifierDescriptionExamples: Round to 0 fraction digits
    -1.50.40.50.61.5
    *"ceil"*Toward positive infinity⬆️ [-1]⬆️ [1]⬆️ [1]⬆️ [1]⬆️ [2]
    *"floor"*Toward negative infinity⬇️ [-2]⬇️ [0]⬇️ [0]⬇️ [0]⬇️ [1]
    *"expand"*Away from zero⬇️ [-2]⬆️ [1]⬆️ [1]⬆️ [1]⬆️ [2]
    *"trunc"*Toward zero⬆️ [-1]⬇️ [0]⬇️ [0]⬇️ [0]⬇️ [1]
    *"halfCeil"*Ties toward positive infinity⬆️ [-1]⬇️ [0]⬆️ [1]⬆️ [1]⬆️ [2]
    *"halfFloor"*Ties toward negative infinity⬇️ [-2]⬇️ [0]⬇️ [0]⬆️ [1]⬇️ [1]
    *"halfExpand"*Ties away from zero⬇️ [-2]⬇️ [0]⬆️ [1]⬆️ [1]⬆️ [2]
    *"halfTrunc"*Ties toward zero⬆️ [-1]⬇️ [0]⬇️ [0]⬆️ [1]⬇️ [1]
    *"halfEven"*Ties toward an even rounding increment multiple⬇️ [-2]⬇️ [0]⬇️ [0]⬆️ [1]⬆️ [2]
    - The examples are illustrative of the unique behaviour of each option. ⬆️ means "resolves toward positive infinity"; ⬇️ means "resolves toward negative infinity". - -

    Finally, Intl.NumberFormat instances have a [[BoundFormat]] internal slot that caches the function returned by the format accessor ().

    @@ -1283,7 +1181,7 @@

    _x_: a non-negative mathematical value, _minPrecision_: an integer in the inclusive interval from 1 to 21, _maxPrecision_: an integer in the inclusive interval from 1 to 21, - _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, + _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, ): a Record with fields [[FormattedString]] (a String), [[RoundedNumber]] (a mathematical value), [[IntegerDigitsCount]] (an integer), and [[RoundingMagnitude]] (an integer)

    @@ -1341,7 +1239,7 @@

    _minFraction_: an integer in the inclusive interval from 0 to 100, _maxFraction_: an integer in the inclusive interval from 0 to 100, _roundingIncrement_: an integer, - _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, + _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, ): a Record with fields [[FormattedString]] (a String), [[RoundedNumber]] (a mathematical value), [[IntegerDigitsCount]] (an integer), and [[RoundingMagnitude]] (an integer)

    @@ -1672,148 +1570,6 @@

    - -

    - GetUnsignedRoundingMode ( - _roundingMode_: a rounding mode, - _sign_: ~negative~ or ~positive~, - ): a specification type from the Unsigned Rounding Mode column of -

    -
    -
    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 Identifier column is _roundingMode_ and the value in the Sign column is _sign_. - - - Conversion from rounding mode to unsigned rounding mode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IdentifierSignUnsigned Rounding Mode
    *"ceil"*~positive~~infinity~
    ~negative~~zero~
    *"floor"*~positive~~zero~
    ~negative~~infinity~
    *"expand"*~positive~~infinity~
    ~negative~~infinity~
    *"trunc"*~positive~~zero~
    ~negative~~zero~
    *"halfCeil"*~positive~~half-infinity~
    ~negative~~half-zero~
    *"halfFloor"*~positive~~half-zero~
    ~negative~~half-infinity~
    *"halfExpand"*~positive~~half-infinity~
    ~negative~~half-infinity~
    *"halfTrunc"*~positive~~half-zero~
    ~negative~~half-zero~
    *"halfEven"*~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_ is _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 _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_ is 0, return _r1_. - 1. Return _r2_. - -
    -

    PartitionNumberRangePattern ( From 36fa7632fa19beef4acef827b798dc7e6ee32ce0 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 20 Feb 2026 17:22:04 -0800 Subject: [PATCH 02/17] TEMP: Move CanonicalizeUValue to ECMA-262 I'm not certain that this has to move. Will discuss with ECMA-262 editors. --- spec/negotiation.html | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/spec/negotiation.html b/spec/negotiation.html index edf76dbe..353e6d1e 100644 --- a/spec/negotiation.html +++ b/spec/negotiation.html @@ -80,25 +80,6 @@

    - -

    - CanonicalizeUValue ( - _ukey_: a Unicode locale extension sequence key defined in Unicode Technical Standard #35 Part 1 Core Section 3.6.1 Key and Type Definitions, - _uvalue_: a String, - ): a String -

    -
    -
    description
    -
    The returned String is the canonical and case-regularized form of _uvalue_ as a value of _ukey_.
    -
    - - 1. Let _lowerValue_ be the ASCII-lowercase of _uvalue_. - 1. Let _canonicalized_ be the String value resulting from canonicalizing _lowerValue_ as a value of key _ukey_ per Unicode Technical Standard #35 Part 1 Core, Annex C LocaleId Canonicalization Section 5 Canonicalizing Syntax, Processing LocaleIds. - 1. NOTE: It is recommended that implementations use the 'u' extension data in common/bcp47 provided by the Common Locale Data Repository (available at https://cldr.unicode.org/). - 1. Return _canonicalized_. - -
    -

    LookupMatchingLocaleByPrefix ( From 4359e61815d1b06fd686e8a23fb1117ac6224aad Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 20 Feb 2026 18:12:53 -0800 Subject: [PATCH 03/17] Normative: Intl Era/Month Code stage 4 --- spec/annexes.html | 6 + spec/datetimeformat.html | 7 + spec/locale-sensitive-functions.html | 1460 ++++++++++++++++++++++++++ spec/locales-currencies-tz.html | 111 ++ spec/negotiation.html | 4 +- spec/normative-references.html | 3 + 6 files changed, 1589 insertions(+), 2 deletions(-) diff --git a/spec/annexes.html b/spec/annexes.html index 95079e09..aca91b12 100644 --- a/spec/annexes.html +++ b/spec/annexes.html @@ -181,6 +181,12 @@

    Implementation Dependent Behaviour

    +
  • + In Temporal: +
      +
    • The calendrical calculations used for calendars other than *"iso8601"* ()
    • +
    +
  • diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index 0388655e..74788d11 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -74,6 +74,9 @@

    1. Let _r_ be _optionsResolution_.[[ResolvedLocale]]. 1. Set _dateTimeFormat_.[[Locale]] to _r_.[[Locale]]. 1. Let _resolvedCalendar_ be _r_.[[ca]]. + 1. If _resolvedCalendar_ is *"islamic"*, then + 1. Set _resolvedCalendar_ to *"islamic-tbla"*. + 1. If the ECMAScript implementation has a mechanism for reporting diagnostic warning messages, a warning should be issued. 1. Set _dateTimeFormat_.[[Calendar]] to _resolvedCalendar_. 1. Set _dateTimeFormat_.[[NumberingSystem]] to _r_.[[nu]]. 1. Let _resolvedLocaleData_ be _r_.[[LocaleData]]. @@ -209,6 +212,10 @@

    Internal slots

    The value of the [[LocaleData]] internal slot is implementation-defined within the constraints described in and the following additional constraints, for all locale values _locale_:

      +
    • + [[LocaleData]].[[<_locale_>]].[[ca]] must be a List consisting of calendar types. + It may include calendar types not listed in . +
    • [[LocaleData]].[[<_locale_>]].[[nu]] must be a List that does not include the values *"native"*, *"traditio"*, or *"finance"*.
    • diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index 90ebd839..079ad46a 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -381,4 +381,1464 @@

      Temporal.ZonedDateTime.prototype.toLocaleString ( [ _locales_ [ , _options_ + + +

      Abstract Operations for Calendar Calculations

      + + +

      + CalendarSupportsEra ( + _calendar_: a calendar type, + ): a Boolean +

      +
      +
      description
      +
      + The following algorithm refers (via ) to the era data from Unicode Technical Standard #35 Part 4 Dates, Calendar Data. +
      +
      + + 1. If _calendar_ is listed in the "Calendar" column of , return *true*. + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Return *false*. + +

      + lists all currently known eras for the current set of calendars, including their aliases, ranges, and kinds. + The canonical source for this table is the data described in Unicode Technical Standard #35 Part 4 Dates, Calendar Data. +

      +

      + The era kind is used by CalendarDateArithmeticYearForEraYear to calculate the arithmetic year ([[Year]]): + An *Era Kind* ~epoch~ means that the era is the epoch era, so 1 Era has an arithmetic year of 1. An *Era Kind* ~negative~ means + that the era is a "negative" era growing + from the epoch, so 1 Era is an arithmetic year of 0, and larger [[EraYear]] values produce smaller, negative arithmetic years. An *Era Kind* of + ~offset~ means that the era is "offset" by a given number (in the *Offset* column), so 1 Era has an arithmetic year of *Offset*. +

      + + Eras + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      CalendarEraAliasesMinimum eraYearMaximum eraYearEra KindOffset
      *"buddhist"**"be"*-∞+∞~epoch~
      *"coptic"**"am"*-∞+∞~epoch~
      *"ethioaa"**"aa"*-∞+∞~epoch~
      *"ethiopic"**"am"*1+∞~epoch~
      *"ethiopic"**"aa"*-∞5500 ~offset~-5499
      *"gregory"**"ce"**"ad"*1+∞~epoch~
      *"gregory"**"bce"**"bc"*1+∞~negative~
      *"hebrew"**"am"*-∞+∞~epoch~
      *"indian"**"shaka"*-∞+∞~epoch~
      *"islamic-civil"**"ah"*1+∞~epoch~
      *"islamic-civil"**"bh"*1+∞~negative~
      *"islamic-tbla"**"ah"*1+∞~epoch~
      *"islamic-tbla"**"bh"*1+∞~negative~
      *"islamic-umalqura"**"ah"*1+∞~epoch~
      *"islamic-umalqura"**"bh"*1+∞~negative~
      *"japanese"**"reiwa"*1+∞~offset~2019
      *"japanese"**"heisei"*131~offset~1989
      *"japanese"**"showa"*164~offset~1926
      *"japanese"**"taisho"*115~offset~1912
      *"japanese"**"meiji"*645~offset~1868
      *"japanese"**"ce"**"ad"*11872~epoch~
      *"japanese"**"bce"**"bc"*1+∞~negative~
      *"persian"**"ap"*-∞+∞~epoch~
      *"roc"**"roc"*1+∞~epoch~
      *"roc"**"broc"*1+∞~negative~
      +
      +
      + + +

      + CanonicalizeEraInCalendar ( + _calendar_: a calendar type that is not *"iso8601"*, + _era_: a String, + ): a String or *undefined* +

      +
      +
      description
      +
      + The following algorithm refers to the era data from Unicode Technical Standard #35 Part 4 Dates, Calendar Data. +
      +
      + + 1. For each row of , except the header row, do + 1. Let _cal_ be the Calendar value of the current row. + 1. If _cal_ is equal to _calendar_, then + 1. Let _canonicalName_ be the Era value of the current row. + 1. If _canonicalName_ is equal to _era_, return _canonicalName_. + 1. Let _aliases_ be a List whose elements are the strings given in the "Aliases" column of the row. + 1. If _aliases_ contains _era_, return _canonicalName_. + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Return *undefined*. + +
      + + +

      + CalendarHasMidYearEras ( + _calendar_: a calendar type that is not *"iso8601"*, + ): a Boolean +

      +
      +
      description
      +
      It returns *true* if the calendar has eras that start in the middle of the year, or *false* if all eras start on a year boundary.
      +
      + + 1. If _calendar_ is *"japanese"*, return *true*. + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Return *false*. + +
      + + +

      + IsValidMonthCodeForCalendar ( + _calendar_: a calendar type that is not *"iso8601"*, + _monthCode_: a month code, + ): a Boolean +

      +
      +
      + + 1. Let _commonMonthCodes_ be « *"M01"*, *"M02"*, *"M03"*, *"M04"*, *"M05"*, *"M06"*, *"M07"*, *"M08"*, *"M09"*, *"M10"*, *"M11"*, *"M12"* ». + 1. If _commonMonthCodes_ contains _monthCode_, return *true*. + 1. If _calendar_ is listed in the "Calendar" column of , then + 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. + 1. Let _specialMonthCodes_ be a List whose elements are the strings given in the "Additional Month Codes" column of _r_. + 1. If _specialMonthCodes_ contains _monthCode_, return *true*. + 1. Return *false*. + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Return *false*. + + + + Additional Month Codes in Calendars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      CalendarAdditional Month CodesLeap to Common Month Transformation
      *"chinese"**"M01L"*, *"M02L"*, *"M03L"*, *"M04L"*, *"M05L"*, *"M06L"*, *"M07L"*, *"M08L"*, *"M09L"*, *"M10L"*, *"M11L"*, *"M12L"*~skip-backward~
      *"coptic"**"M13"*
      *"dangi"**"M01L"*, *"M02L"*, *"M03L"*, *"M04L"*, *"M05L"*, *"M06L"*, *"M07L"*, *"M08L"*, *"M09L"*, *"M10L"*, *"M11L"*, *"M12L"*~skip-backward~
      *"ethioaa"**"M13"*
      *"ethiopic"**"M13"*
      *"hebrew"**"M05L"*~skip-forward~
      +
      +
      + + +

      + YearContainsMonthCode ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _monthCode_: a month code, + ): a Boolean +

      +
      +
      description
      +
      + It returns whether the given _monthCode_ exists in _arithmeticYear_ of _calendar_. +
      +
      +

      It performs the following steps when called:

      + + 1. Assert: IsValidMonthCodeForCalendar(_calendar_, _monthCode_) is *true*. + 1. If ! ParseMonthCode(_monthCode_).[[IsLeap]] is *false*, return *true*. + 1. Return whether the leap month indicated by _monthCode_ exists in the year _arithmeticYear_ in _calendar_, using calendar-dependent behaviour. + +
      + + +

      + ConstrainMonthCode ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _monthCode_: a month code, + _overflow_: ~constrain~ or ~reject~, + ): either a normal completion containing a month code or a throw completion +

      +
      +
      description
      +
      + It returns the month code in _arithmeticYear_ of _calendar_ that best matches the given _monthCode_. If _monthCode_ does not exist in _arithmeticYear_, it is constrained to the best common month if _overflow_ is ~constrain~, or an error is thrown if _overflow_ is ~reject~. +
      +
      +

      It performs the following steps when called:

      + + 1. Assert: IsValidMonthCodeForCalendar(_calendar_, _monthCode_) is *true*. + 1. If YearContainsMonthCode(_calendar_, _arithmeticYear_, _monthCode_) is *true*, return _monthCode_. + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Assert: _calendar_ is listed in the "Calendar" column of . + 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. + 1. Let _shiftType_ be the value given in the "Leap to Common Month Transformation" column of _r_. + 1. If _shiftType_ is ~skip-backward~, then + 1. Return CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). + 1. Else, + 1. Assert: _monthCode_ is *"M05L"*. + 1. Return *"M06"*. + +
      + + +

      + MonthCodeToOrdinal ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _monthCode_: a month code, + ): an integer +

      +
      +
      description
      +
      + It returns the ordinal month number for _monthCode_ in _arithmeticYear_ of _calendar_. The given _monthCode_ must exist in the given year. +
      +
      +

      It performs the following steps when called:

      + + 1. Assert: YearContainsMonthCode(_calendar_, _arithmeticYear_, _monthCode_) is *true*. + 1. Let _monthsBefore_ be 0. + 1. Let _number_ be 1. + 1. Let _isLeap_ be *false*. + 1. Let _r_ be the row in which the _calendar_ is in the Calendar column. + 1. If the "Leap to Common Month Transformation" column of _r_ is empty, then + 1. Return ! ParseMonthCode(_monthCode_).[[MonthNumber]]. + 1. Assert: The "Additional Month Codes" column of _r_ does not contain *"M00L"* or *"M13"*. + 1. Assert: This algorithm will return before the following loop terminates by failing its condition. + 1. Repeat, while _number_ ≤ 12, + 1. Let _currentMonthCode_ be CreateMonthCode(_number_, _isLeap_). + 1. If IsValidMonthCodeForCalendar(_calendar_, _currentMonthCode_) is *true* and YearContainsMonthCode(_calendar_, _arithmeticYear_, _currentMonthCode_) is *true*, then + 1. Set _monthsBefore_ to _monthsBefore_ + 1. + 1. If _currentMonthCode_ is _monthCode_, then + 1. Return _monthsBefore_. + 1. If _isLeap_ is *false*, then + 1. Set _isLeap_ to *true*. + 1. Else, + 1. Set _isLeap_ to *false*. + 1. Set _number_ to _number_ + 1. + +
      + + +

      + CalendarDaysInMonth ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _ordinalMonth_: a positive integer, + ): a positive integer +

      +
      +
      description
      +
      + The returned value represents the number of days in the _calendar_-specific _arithmeticYear_ and _ordinalMonth_. +
      +
      +

      It performs the following steps when called:

      + + 1. Let _isoDate_ be ! CalendarIntegersToISO(_calendar_, _arithmeticYear_, _ordinalMonth_, 1). + 1. Return CalendarISOToDate(_calendar_, _isoDate_).[[DaysInMonth]]. + +
      + + +

      + CalendarDateEra ( + _calendar_: a calendar type that is not *"iso8601"*, + _date_: an ISO Date Record, + ): a String or *undefined* +

      +
      +
      description
      +
      It performs implementation-defined processing to find the era for the date corresponding to _date_ in the context of the calendar represented by _calendar_ and returns a lowercase String value representing that era, or *undefined* for calendars that do not have eras.
      +
      + + 1. If CalendarSupportsEra(_calendar_) is *false*, return *undefined*. + 1. Let _era_ be an implementation-defined String indicating the era corresponding to _date_ in the context of the calendar represented by _calendar_. + 1. Return CanonicalizeEraInCalendar(_calendar_, _era_). + +
      + + +

      + CalendarDateEraYear ( + _calendar_: a calendar type that is not *"iso8601"*, + _date_: an ISO Date Record, + ): an integer or *undefined* +

      +
      +
      description
      +
      It performs implementation-defined processing to find the era for the date corresponding to _date_ in the context of the calendar represented by _calendar_ and returns an integer representing the ordinal position of the year of _date_ in that era, or *undefined* for calendars that do not have eras.
      +
      +

    + + 1. If CalendarSupportsEra(_calendar_) is *false*, return *undefined*. + 1. Return an implementation-defined integer indicating the era year corresponding to _date_ in the context of the calendar represented by _calendar_. + + + + +

    + CalendarDateArithmeticYear ( + _calendar_: a calendar type that is not *"iso8601"*, + _date_: an ISO Date Record, + ): an integer +

    +
    +
    description
    +
    It performs implementation-defined processing to find the arithmetic year for the date corresponding to _date_ in the context of the calendar represented by _calendar_.
    +
    + + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. + 1. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_. + 1. Let _epochDate_ be the first day of the calendar year starting in ISO year _epochYear_ in the calendar represented by _calendar_, according to implementation-defined processing. + 1. Let _newYear_ be the first day of the calendar year of _date_ in the calendar represented by _calendar_, according to implementation-defined processing. + 1. Let _arithmeticYear_ be the implementation-defined signed number of whole years between _epochDate_ and _newYear_ in the calendar represented by _calendar_. + 1. Return _arithmeticYear_. + + + + Epoch years + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CalendarEpoch ISO Year
    *"buddhist"*-543
    *"chinese"*0
    *"coptic"*283
    *"dangi"*0
    *"ethioaa"*-5493
    *"ethiopic"*7
    *"gregory"*0
    *"hebrew"*-3761
    *"indian"*78
    *"islamic-civil"*621
    *"islamic-tbla"*621
    *"islamic-umalqura"*621
    *"japanese"*0
    *"persian"*621
    *"roc"*1911
    +
    +
    + + +

    + CalendarDateArithmeticYearForEraYear ( + _calendar_: a calendar type that has eras, + _era_: a String, + _eraYear_: an integer, + ): an integer +

    +
    +
    description
    +
    It produces the arithmetic year for a given set of _era_, _eraYear_ in _calendar_, a calendar that includes an era named _era_.
    +
    + + 1. Let _era_ be CanonicalizeEraInCalendar(_calendar_, _era_). + 1. Assert: _era_ is not *undefined*. + 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_ and a value in the Era column matching _era_. + 1. Let _eraKind_ be the value given in the "Era Kind" column of _r_. + 1. Let _offset_ be the value given in the "Offset" column of _r_. + 1. If _eraKind_ is ~epoch~, return _eraYear_. + 1. If _eraKind_ is ~negative~, return 1 - _eraYear_. + 1. Assert: _eraKind_ is ~offset~. + 1. Assert: _offset_ is an integer. + 1. Return _offset_ + _eraYear_ - 1. + +
    + + +

    + CalendarIntegersToISO ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _ordinalMonth_: a positive integer, + _day_: a positive integer, + ): either a normal completion containing an ISO Date Record or a throw completion +

    +
    +
    description
    +
    + It returns an ISO Date Record that corresponds with the given _calendar_-specific _arithmeticYear_, _ordinalMonth_, and _day_. +
    +
    +

    It performs the following steps when called:

    + + 1. If _arithmeticYear_, _ordinalMonth_, and _day_ do not form a valid date in _calendar_, throw a *RangeError* exception. + 1. Let _isoDate_ be an ISO Date Record such that CalendarISOToDate(_calendar_, _isoDate_) returns a Calendar Date Record whose [[Year]], [[Month]], and [[Day]] field values respectively equal _arithmeticYear_, _ordinalMonth_, and _day_. + 1. NOTE: No known calendars have repeated dates that would cause _isoDate_ to be ambiguous between two ISO Date Records. + 1. Return _isoDate_. + +
    + + +

    Calendar Date Records

    +

    + A Calendar Date Record is a Record value used to represent a valid calendar date in a non-ISO 8601 calendar. + Calendar Date Records are produced by the abstract operation CalendarISOToDate. +

    +

    Calendar Date Records have the fields listed in .

    +

    This definition supersedes the one in .

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Field NameValueMeaning
    [[Era]]a String or *undefined* + A lowercase String value representing the date's era, or *undefined* for calendars that do not have eras.
    + The value of this field for a calendar type _calendar_ should be the result of calling CalendarDateEra(_calendar_, _date_), where _date_ is the [[ISODate]] field of a Temporal.PlainDateTime, Temporal.PlainDate, or Temporal.PlainYearMonth value corresponding to the date. +
    [[EraYear]]an integer or *undefined* + The ordinal position of the date's year within its era, or *undefined* for calendars that do not have eras.
    + The value of this field for a calendar type _calendar_ should be the result of calling CalendarDateEraYear(_calendar_, _date_), where _date_ is the [[ISODate]] field of a Temporal.PlainDateTime, Temporal.PlainDate, or Temporal.PlainYearMonth value corresponding to the date. + + Era years are 1-indexed for many calendars, but not all (e.g., the eras of the Burmese calendar, not currently available in CLDR, each start with a year 0). Years can also advance opposite the flow of time (as for BCE years in the Gregorian calendar). + +
    [[Year]]an integer + The date's arithmetic year, which is the year relative to the first day of a calendar-specific epoch year, given in .
    + The value of this field for a calendar type _calendar_ should be the result of calling CalendarDateArithmeticYear(_calendar_, _date_), where _date_ is the [[ISODate]] field of a Temporal.PlainDateTime, Temporal.PlainDate, or Temporal.PlainYearMonth value corresponding to the date. + The arithmetic year is relative to the first day of the calendar's epoch year, so if the epoch era starts in the middle of the year, the year will be the same value before and after the start date of the era. +
    [[Month]]a positive integer + The 1-based ordinal position of the date's month within its year. + + When the number of months in a year of the calendar is variable, a different value can be returned for dates that are part of the same month in different years. For example, in the Hebrew calendar, 1 Nisan 5781 is associated with value 7 while 1 Nisan 5782 is associated with value 8 because 5782 is a leap year and Nisan follows the insertion of Adar I. + +
    [[MonthCode]]a String + The month code of the date's month. The month code for a month that is not a leap month and whose 1-based ordinal position in a common year of the calendar (i.e., a year that is not a leap year) is _n_ should be the string-concatenation of *"M"* and ToZeroPaddedDecimalString(_n_, 2), and the month code for a month that is a leap month inserted after a month whose 1-based ordinal position in a common year of the calendar is _p_ should be the string-concatenation of *"M"*, ToZeroPaddedDecimalString(_p_, 2), and *"L"*. + + For example, in the Hebrew calendar, the month code of Adar (and Adar II, in leap years) is *"M06"* and the month code of Adar I (the leap month inserted before Adar II) is *"M05L"*. Theoretically, in a calendar with a leap month at the start of some years, the month code of that month would be *"M00L"*. + +
    [[Day]]a positive integer + The 1-based ordinal position of the date's day within its month. +
    [[DayOfWeek]]a positive integer + The day of the week corresponding to the date. The value should be 1-based, where 1 is the day corresponding to Monday in the given calendar. +
    [[DayOfYear]]a positive integer + The 1-based ordinal position of the date's day within its year. +
    [[WeekOfYear]]a Year-Week Record +

    The date's calendar week of year, and the corresponding week calendar year.

    +

    The Year-Week Record's [[Week]] field should be 1-based.

    +

    The Year-Week Record's [[Year]] field is an arithmetic year as in the Calendar Date Record's [[Year]] field, not relative to an era as in [[EraYear]].

    +

    + Usually the Year-Week Record's [[Year]] field will contain the same value as the Calendar Date Record's [[Year]] field, but may contain the previous or next year if the week number in the Year-Week Record's [[Week]] field overlaps two different years. + See also ISOWeekOfYear. +

    +

    The Year-Week Record contains *undefined* in [[Week]] and [[Year]] field for calendars that do not have a well-defined week numbering system.

    + + Currently, of the calendars supported in this specification, only *"iso8601"* has a well-defined, locale-independent week numbering system. + For all other calendars, the Year-Week Record fields are *undefined*. + +
    [[DaysInWeek]]a positive integer +

    The number of days in the primary notion of week used by the calendar. For all calendars currently supported by this specification, this number is 7.

    + + Some calendars have alternate cyclic notions that are similar to the 7-day week; many of them have multiple (like the Javanese calendar). This specification does not cover such calendars but can be extended to do so in the future. + +
    [[DaysInMonth]]a positive integerThe number of days in the date's month.
    [[DaysInYear]]a positive integerThe number of days in the date's year.
    [[MonthsInYear]]a positive integerThe number of months in the date's year.
    [[InLeapYear]]a Boolean + *true* if the date falls within a leap year, and *false* otherwise. + + A "leap year" is a year that contains more days than other years (for solar or lunar calendars) or more months than other years (for lunisolar calendars like Hebrew or Chinese). + Some calendars, especially lunisolar ones, have further variation in year length that is not represented in the output of this operation (e.g., the Hebrew calendar includes common years with 353, 354, or 355 days and leap years with 383, 384, or 385 days). + +
    +
    +
    + + +

    + CalendarMonthsInYear ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + ): a positive integer +

    +
    +
    description
    +
    + The returned value represents the number of months in the _calendar_-specific _arithmeticYear_. +
    +
    +

    It performs the following steps when called:

    + + 1. Let _isoDate_ be ! CalendarIntegersToISO(_calendar_, _arithmeticYear_, 1, 1). + 1. Return CalendarISOToDate(_calendar_, _isoDate_).[[MonthsInYear]]. + +
    + + +

    + BalanceNonISODate ( + _calendar_: a calendar type that is not *"iso8601"*, + _arithmeticYear_: an integer, + _ordinalMonth_: a positive integer, + _day_: an integer, + ): a Record with fields [[Year]] (an integer), [[Month]] (a positive integer), and [[Day]] (a positive integer) +

    +
    +
    description
    +
    + It interprets the given _arithmeticYear_, potentially out-of-range _ordinalMonth_, and potentially out-of-range _day_ as arithmetical values in the given _calendar_ and returns in-range values by overflowing out-of-range _ordinalMonth_ or _day_ values into the next-highest unit. + This date may correspond to a date in the ISO calendar that is outside the range given by ISODateTimeWithinLimits. +
    +
    +

    It performs the following steps when called:

    + + 1. Let _resolvedYear_ be _arithmeticYear_. + 1. Let _resolvedMonth_ be _ordinalMonth_. + 1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _resolvedYear_). + 1. Repeat, while _resolvedMonth_ ≤ 0, + 1. Set _resolvedYear_ to _resolvedYear_ - 1. + 1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_). + 1. Set _resolvedMonth_ to _resolvedMonth_ + _monthsInYear_. + 1. Repeat, while _resolvedMonth_ > _monthsInYear_, + 1. Set _resolvedMonth_ to _resolvedMonth_ - _monthsInYear_. + 1. Set _resolvedYear_ to _resolvedYear_ + 1. + 1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_). + 1. Let _resolvedDay_ be _day_. + 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_). + 1. Repeat, while _resolvedDay_ ≤ 0, + 1. Set _resolvedMonth_ to _resolvedMonth_ - 1. + 1. If _resolvedMonth_ is 0, then + 1. Set _resolvedYear_ to _resolvedYear_ - 1. + 1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_). + 1. Set _resolvedMonth_ to _monthsInYear_. + 1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_). + 1. Set _resolvedDay_ to _resolvedDay_ + _daysInMonth_. + 1. Repeat, while _resolvedDay_ > _daysInMonth_, + 1. Set _resolvedDay_ to _resolvedDay_ - _daysInMonth_. + 1. Set _resolvedMonth_ to _resolvedMonth_ + 1. + 1. If _resolvedMonth_ > _monthsInYear_, then + 1. Set _resolvedYear_ to _resolvedYear_ + 1. + 1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_). + 1. Set _resolvedMonth_ to 1. + 1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_). + 1. Return the Record { [[Year]]: _resolvedYear_, [[Month]]: _resolvedMonth_, [[Day]]: _resolvedDay_ }. + +
    + + +

    + NonISODateSurpasses ( + _calendar_: a calendar type that is not *"iso8601"*, + _sign_: -1 or 1, + _fromIsoDate_: an ISO Date Record, + _toIsoDate_: an ISO Date Record, + _years_: an integer, + _months_: an integer, + _weeks_: an integer, + _days_: an integer, + ): a Boolean +

    +
    +
    description
    +
    + The return value indicates whether the date _date1_, the result of adding the duration denoted by _years_, _months_, _weeks_, and _days_ to _fromIsoDate_ in the calendar system denoted by _calendar_, surpasses _toIsoDate_ in the direction denoted by _sign_. + If _weeks_ and _days_ are both zero, then _date1_ need not exist (for example, it could be February 30). +
    +
    +

    It performs the following steps when called:

    + + 1. Let _parts_ be CalendarISOToDate(_calendar_, _fromIsoDate_). + 1. Let _calDate2_ be CalendarISOToDate(_calendar_, _toIsoDate_). + 1. Let _y0_ be _parts_.[[Year]] + _years_. + 1. If CompareSurpasses(_sign_, _y0_, _parts_.[[MonthCode]], _parts_.[[Day]], _calDate2_) is *true*, return *true*. + 1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], ~constrain~)). + 1. Let _monthsAdded_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _months_, 1). + 1. If CompareSurpasses(_sign_, _monthsAdded_.[[Year]], _monthsAdded_.[[Month]], _parts_.[[Day]], _calDate2_) is *true*, return *true*. + 1. If _weeks_ = 0 and _days_ = 0, return *false*. + 1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _monthsAdded_.[[Year]], _monthsAdded_.[[Month]] + 1, 0). + 1. Let _baseDay_ be _parts_.[[Day]]. + 1. If _baseDay_ ≤ _endOfMonth_.[[Day]], then + 1. Let _regulatedDay_ be _baseDay_. + 1. Else, + 1. Let _regulatedDay_ be _endOfMonth_.[[Day]]. + 1. Let _daysInWeek_ be 7 (the number of days in a week for all supported calendars). + 1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + _daysInWeek_ * _weeks_ + _days_). + 1. Return CompareSurpasses(_sign_, _balancedDate_.[[Year]], _balancedDate_.[[Month]], _balancedDate_.[[Day]], _calDate2_). + +
    + + +

    + NonISODateAdd ( + _calendar_: a calendar type that is not *"iso8601"*, + _isoDate_: an ISO Date Record, + _duration_: a Date Duration Record, + _overflow_: ~constrain~ or ~reject~, + ): either a normal completion containing an ISO Date Record or a throw completion +

    +
    +
    description
    +
    + The operation performs processing to add _duration_ to _date_ in the context of the calendar represented by _calendar_ and returns the corresponding day, month and year of the result in the ISO 8601 calendar values as an ISO Date Record. + It may throw a *RangeError* exception if _overflow_ is ~reject~ and the resulting month or day would need to be clamped in order to form a valid date in _calendar_. +
    +
    +

    All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateAdd for *"iso8601"*.

    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Let _parts_ be CalendarISOToDate(_calendar_, _isoDate_). + 1. Let _y0_ be _parts_.[[Year]] + _duration_.[[Years]]. + 1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ? ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], _overflow_)). + 1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _duration_.[[Months]] + 1, 0). + 1. Let _baseDay_ be _parts_.[[Day]]. + 1. If _baseDay_ ≤ _endOfMonth_.[[Day]], then + 1. Let _regulatedDay_ be _baseDay_. + 1. Else, + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _regulatedDay_ be _endOfMonth_.[[Day]]. + 1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _duration_.[[Weeks]] + _duration_.[[Days]]). + 1. Let _result_ be ? CalendarIntegersToISO(_calendar_, _balancedDate_.[[Year]], _balancedDate_.[[Month]], _balancedDate_.[[Day]]). + 1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception. + 1. Return _result_. + +
    + + +

    + NonISODateUntil ( + _calendar_: a calendar type that is not *"iso8601"*, + _one_: an ISO Date Record, + _two_: an ISO Date Record, + _largestUnit_: a date unit, + ): a Date Duration Record +

    +
    +
    description
    +
    + It determines the difference between the dates _one_ and _two_ using the years, months, and weeks reckoning of _calendar_. + No fields larger than _largestUnit_ will be non-zero in the resulting Date Duration Record. +
    +
    +

    All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateUntil for *"iso8601"*.

    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Let _sign_ be -1 × CompareISODate(_one_, _two_). + 1. If _sign_ = 0, return ZeroDateDuration(). + 1. Let _years_ be 0. + 1. If _largestUnit_ is ~year~, then + 1. Let _candidateYears_ be _sign_. + 1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _candidateYears_, 0, 0, 0) is *false*, + 1. Set _years_ to _candidateYears_. + 1. Set _candidateYears_ to _candidateYears_ + _sign_. + 1. Let _months_ be 0. + 1. If _largestUnit_ is ~year~ or _largestUnit_ is ~month~, then + 1. Let _candidateMonths_ be _sign_. + 1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _candidateMonths_, 0, 0) is *false*, + 1. Set _months_ to _candidateMonths_. + 1. Set _candidateMonths_ to _candidateMonths_ + _sign_. + 1. Let _weeks_ be 0. + 1. If _largestUnit_ is ~week~, then + 1. Let _candidateWeeks_ be _sign_. + 1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _candidateWeeks_, 0) is *false*, + 1. Set _weeks_ to _candidateWeeks_. + 1. Set _candidateWeeks_ to _candidateWeeks_ + sign. + 1. Let _days_ be 0. + 1. Let _candidateDays_ be _sign_. + 1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _weeks_, _candidateDays_) is *false*, + 1. Set _days_ to _candidateDays_. + 1. Set _candidateDays_ to _candidateDays_ + _sign_. + 1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). + +
    + + +

    + NonISOCalendarDateToISO ( + _calendar_: a calendar type that is not *"iso8601"*, + _fields_: a Calendar Fields Record, + _overflow_: ~constrain~ or ~reject~, + ): either a normal completion containing an ISO Date Record or a throw completion +

    +
    +
    description
    +
    + It converts _fields_, which represents either a date or a year and month in the built-in calendar identified by _calendar_, to a corresponding representative date in the ISO 8601 calendar, subject to overflow correction specified by _overflow_. + For ~reject~, values that do not form a valid date cause an exception to be thrown, as described below. + For ~constrain~, values that do not form a valid date are clamped to their respective valid range. +
    +
    +

    Like RegulateISODate, the operation throws a *RangeError* exception when _overflow_ is ~reject~ and the date described by _fields_ does not exist.

    +

    Clamping an invalid date to the correct range when _overflow_ is ~constrain~ is a behaviour specific to each calendar other than *"iso8601"*, but all calendars follow this guideline.

    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Assert: _fields_.[[Year]], _fields_.[[Month]], and _fields_.[[Day]] are not ~unset~. + 1. If _fields_.[[MonthCode]] is not ~unset~, then + 1. Perform ? ConstrainMonthCode(_calendar_, _fields_.[[Year]], _fields_.[[MonthCode]], _overflow_). + 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _fields_.[[Month]]). + 1. If _fields_.[[Day]] > _daysInMonth_, then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _day_ be _daysInMonth_. + 1. Else, + 1. Let _day_ be _fields_.[[Day]]. + 1. Return ? CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). + +
    + + +

    + NonISOMonthDayToISOReferenceDate ( + _calendar_: a calendar type that is not *"iso8601"*, + _fields_: a Calendar Fields Record, + _overflow_: ~constrain~ or ~reject~, + ): either a normal completion containing an ISO Date Record or a throw completion +

    +
    +
    description
    +
    + It performs implementation-defined processing to convert _fields_, which represents a calendar date without a year (i.e., month code and day pair, or equivalent) in the built-in calendar identified by _calendar_, to a corresponding reference date in the ISO 8601 calendar as described below, subject to overflow correction specified by _overflow_. + For ~reject~, values that do not form a valid date cause an exception to be thrown. + For ~constrain~, values that do not form a valid date are clamped to their respective valid range as in CalendarDateToISO. +
    +
    +

    + The fields of the returned ISO Date Record represent a reference date in the ISO 8601 calendar that, when converted to _calendar_, corresponds to the month code and day of _fields_ in an arbitrary but deterministically chosen reference year. + The reference year is almost always 1972 (the first ISO 8601 leap year after the epoch), with exceptions for calendars where some dates (e.g. leap days or days in leap months) didn't occur during that ISO 8601 year. + These exceptions are listed in . +

    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Assert: _fields_.[[Month]] and _fields_.[[Day]] are not ~unset~. + 1. Assert: If _fields_.[[MonthCode]] is ~unset~, _fields_.[[Year]] is not ~unset~. + 1. Let _monthCode_ be _fields_.[[MonthCode]]. + 1. If _calendar_ is *"chinese"* or *"dangi"*, then + 1. NOTE: This special case handles combinations of month and day that theoretically could occur but are not known to have occurred historically and cannot be accurately calculated to occur in the future, even if it may be possible to construct a PlainDate with such combinations due to inaccurate approximations. This is explicitly mentioned here because as time goes on, these dates may become known to have occurred historically, or may be more accurately calculated to occur in the future. + 1. Let _daysInMonth_ be 30. + 1. If _fields_.[[Day]] > _daysInMonth_, then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _day_ be _daysInMonth_. + 1. Else, + 1. Let _day_ be _fields_.[[Day]]. + 1. If _fields_.[[Year]] is not ~unset~, then + 1. [declared="isoDate"] If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. + 1. NOTE: The above step exists so as not to require calculating whether the month and day described in _fields_ exist in user-provided years arbitrarily far in the future or past. + 1. If _monthCode_ is ~unset~, then + 1. Let _fieldsISODate_ be ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). + 1. Set _monthCode_ to NonISOCalendarISOToDate(_calendar_, _fieldsISODate_).[[MonthCode]]. + 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_. + 1. If the "Reference Year (Days 1-29)" column of _row_ is "—", or _day_ = 30 and the "Reference Year (Day 30)" column of _row_ is "—", then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Set _monthCode_ to CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). + 1. Else, + 1. If _fields_.[[Year]] is not ~unset~, then + 1. If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. + 1. If _monthCode_ is not ~unset~, then + 1. Set _monthCode_ to ? ConstrainMonthCode(_calendar_, _fields_.[[Year]], _monthCode_, _overflow_). + 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _fields_.[[Month]]). + 1. Else, + 1. Assert: _monthCode_ is not ~unset~. + 1. Let _daysInMonth_ be the maximum number of days in the month described by _monthCode_ in any year. + 1. If _fields_.[[Day]] > _daysInMonth_, then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _day_ be _daysInMonth_. + 1. Else, + 1. Let _day_ be _fields_.[[Day]]. + 1. If _monthCode_ is ~unset~, then + 1. Let _fieldsISODate_ be ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). + 1. Set _monthCode_ to NonISOCalendarISOToDate(_calendar_, _fieldsISODate_).[[MonthCode]]. + 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_ (or "any" or "others" if there is no row matching _monthCode_ exactly). + 1. Assert: _monthCode_ and _day_ do not match an entry in _row_ that is "—". + 1. Let _referenceYear_ be the ISO reference year for _monthCode_ and _day_ as described in _row_. + 1. Return the latest possible ISO Date Record _isoDate_ such that _isoDate_.[[Year]] = _referenceYear_ and NonISOCalendarISOToDate(_calendar_, _isoDate_) returns a Calendar Date Record whose [[MonthCode]] and [[Day]] field values respectively equal _monthCode_ and _day_. + + + + ISO Reference Years + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Calendar TypeMonth CodeReference Year (Days 1–29)Reference Year (Day 30)
    *"buddhist"*, *"gregory"*, *"indian"*, *"japanese"*, *"persian"*, *"roc"*anyAny day including 31: 1972
    *"chinese"*, *"dangi"*M0119721970
    M01L
    M021972
    M02L1947
    M031972*"chinese"*: 1966
    + *"dangi"*: 1968
    M03L19661955
    M0419721970
    M04L19631944
    M051972
    M05L19711952
    M0619721971
    M06L19601941
    M071972
    M07L19681938
    M0819721971
    M08L1957
    M091972
    M09L2014
    M101972
    M10L1984
    M1119721970
    M11LDays 1–10: 2033
    + Days 11–29: 2034
    M121972
    M12L
    *"coptic"*, *"ethioaa"*, *"ethiopic"*M13Days 1–5: 1972
    + Day 6: 1971
    + Days 7–29: —
    others1972
    *"hebrew"*M0219721971
    M0319721971
    M05L1970
    others1972
    *"islamic-civil"*, *"islamic-tbla"*M1219721971
    others1972
    *"islamic-umalqura"*M011972
    M0219721970
    M0319721971
    M041972
    M0519721971
    M061972
    M0719721969
    M081972
    M091972
    M1019721970
    M111972
    M1219721971
    +
    +
    + + +

    + CalendarExtraFields ( + _calendar_: a calendar type, + _fields_: a List of values from the "Enumeration Key" column of , + ): a List of values from the "Enumeration Key" column of +

    +
    +
    description
    +
    It characterizes calendar-specific fields that are relevant for the provided _fields_ in the built-in calendar identified by _calendar_.
    +
    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. If _fields_ contains an element equal to ~year~ and CalendarSupportsEra(_calendar_) is *true*, then + 1. Return « ~era~, ~era-year~ ». + 1. Return an empty List. + +
    + + +

    + NonISOFieldKeysToIgnore ( + _calendar_: a calendar type that is not *"iso8601"*, + _keys_: a List of values from the "Enumeration Key" column of , + ): a List of values from the "Enumeration Key" column of +

    +
    +
    description
    +
    + It performs implementation-defined processing to determine which calendar date fields changing any of the fields named in _keys_ can potentially conflict with or invalidate, for the given _calendar_. + A field always invalidates at least itself. +
    +
    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Let _ignoredKeys_ be a copy of _keys_. + 1. For each element _key_ of _keys_, do + 1. If _key_ is ~month~, append ~month-code~ to _ignoredKeys_. + 1. If _key_ is ~month-code~, append ~month~. + 1. If _key_ is one of ~era~, ~era-year~, or ~year~ and CalendarSupportsEra(_calendar_) is *true*, then + 1. Append ~era~, ~era-year~, and ~year~ to _ignoredKeys_. + 1. If _key_ is one of ~day~, ~month~, or ~month-code~ and CalendarHasMidYearEras(_calendar_) is *true*, then + 1. Append ~era~ and ~era-year~ to _ignoredKeys_. + 1. NOTE: While _ignoredKeys_ can have duplicate elements, this is not intended to be meaningful. This specification only checks whether particular keys are or are not members of the list. + 1. Return _ignoredKeys_. + +
    + + +

    + NonISOResolveFields ( + _calendar_: a calendar type that is not *"iso8601"*, + _fields_: a Calendar Fields Record, + _type_: ~date~, ~year-month~, or ~month-day~, + ): either a normal completion containing ~unused~ or a throw completion +

    +
    +
    description
    +
    + It performs implementation-defined processing to validate that _fields_ (which describes a date or partial date in the built-in calendar identified by _calendar_) is sufficiently complete to satisfy _type_ and not internally inconsistent, and mutates _fields_ into acceptable input for CalendarDateToISO or CalendarMonthDayToISOReferenceDate by merging data that can be represented in multiple forms into standard fields and removing redundant fields (for example, merging [[Era]] and [[EraYear]] into [[Year]]). +
    +
    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Let _needsYear_ be *false*. + 1. If _type_ is ~date~ or _type_ is ~year-month~, set _needsYear_ to *true*. + 1. If _fields_.[[MonthCode]] is ~unset~, set _needsYear_ to *true*. + 1. If _fields_.[[Month]] is not ~unset~, set _needsYear_ to *true*. + 1. Let _needsDay_ be *false*. + 1. If _type_ is ~date~ or _type_ is ~month-day~, set _needsDay_ to *true*. + 1. If _needsYear_ is *true*, then + 1. If _fields_.[[Year]] is ~unset~, then + 1. If CalendarSupportsEra(_calendar_) is *false*, throw a *TypeError* exception. + 1. If _fields_.[[Era]] is ~unset~ or _fields_.[[EraYear]] is ~unset~, throw a *TypeError* exception. + 1. If CalendarSupportsEra(_calendar_) is *true*, then + 1. If _fields_.[[Era]] is not ~unset~ and _fields_.[[EraYear]] is ~unset~, throw a *TypeError* exception. + 1. If _fields_.[[EraYear]] is not ~unset~ and _fields_.[[Era]] is ~unset~, throw a *TypeError* exception. + 1. If _needsDay_ is *true* and _fields_.[[Day]] is ~unset~, throw a *TypeError* exception. + 1. If _fields_.[[Month]] is ~unset~ and _fields_.[[MonthCode]] is ~unset~, throw a *TypeError* exception. + 1. If CalendarSupportsEra(_calendar_) is *true* and _fields_.[[EraYear]] is not ~unset~, then + 1. Let _canonicalEra_ be CanonicalizeEraInCalendar(_calendar_, _fields_.[[Era]]). + 1. If _canonicalEra_ is *undefined*, throw a *RangeError* exception. + 1. Let _arithmeticYear_ be CalendarDateArithmeticYearForEraYear(_calendar_, _canonicalEra_, _fields_.[[EraYear]]). + 1. If _fields_.[[Year]] is not ~unset~, and _fields_.[[Year]] ≠ _arithmeticYear_, throw a *RangeError* exception. + 1. Set _fields_.[[Year]] to _arithmeticYear_. + 1. Set _fields_.[[Era]] to ~unset~. + 1. Set _fields_.[[EraYear]] to ~unset~. + 1. NOTE: _fields_.[[Era]] and _fields_.[[EraYear]] are erased in order to allow a lenient interpretation of out-of-bounds values, which is particularly useful for consistent interpretation of dates in calendars with regnal eras. + 1. If _fields_.[[MonthCode]] is not ~unset~, then + 1. If IsValidMonthCodeForCalendar(_calendar_, _fields_.[[MonthCode]]) is *false*, throw a *RangeError* exception. + 1. If _fields_.[[Year]] is not ~unset~, then + 1. If YearContainsMonthCode(_calendar_, _fields_.[[Year]], _fields_.[[MonthCode]]) is *true*, let _constrainedMonthCode_ be _fields_.[[MonthCode]]; else let _constrainedMonthCode_ be ! ConstrainMonthCode(_calendar_, _fields_.[[Year]], _fields_.[[MonthCode]], ~constrain~). + 1. Let _month_ be MonthCodeToOrdinal(_calendar_, _fields_.[[Year]], _constrainedMonthCode_). + 1. If _fields_.[[Month]] is not ~unset~ and _fields_.[[Month]] ≠ _month_, throw a *RangeError* exception. + 1. Set _fields_.[[Month]] to _month_. + 1. NOTE: _fields_.[[MonthCode]] is intentionally not overwritten with _constrainedMonthCode_. Pending the "overflow" parameter in CalendarDateToISO or CalendarMonthDayToISOReferenceDate, a month code not occurring in _fields_.[[Year]] may cause that operation to throw. However, if _fields_.[[Month]] is present, it must agree with the constrained month code. + 1. Assert: _fields_.[[Era]] and _fields_.[[EraYear]] are ~unset~. + 1. Assert: If _needsYear_ is *true*, _fields_.[[Year]] is not ~unset~. + 1. Assert: _fields_.[[Month]] is not ~unset~. + 1. Assert: If _needsDay_ is *true*, _fields_.[[Day]] is not ~unset~. + 1. Return ~unused~. + +
    + diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index e45af632..34475f0e 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -515,6 +515,117 @@

    AvailableCanonicalCollations ( ): a List of Strings

    + +

    Calendar Types

    +

    + ECMA-262 describes calendar types, of which *"iso8601"* is required to be supported. + This specification additionally requires ECMAScript implementations to support calendar types corresponding with those of the Unicode Common Locale Data Repository (CLDR). +

    + +

    + The following table lists the calendar types described in CLDR, along with implementation notes and sources where necessary. + All calendar types must use a proleptic reckoning, meaning that the current rules for calendrical calculations are extended indefinitely into the past, ignoring dates when historical calendar reforms happened. +

    + +

    + For example, at various points from the 16th to the 20th century, various locales in Europe changed from the Julian to the Gregorian calendar, skipping some dates in the process. + Since the *"gregory"* calendar is proleptic, no dates are skipped; all dates are in the "new system," even if it wasn't used at the time. +

    + + + Calendar types described in CLDR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Calendar TypeDescription and implementation notes
    *"buddhist"*Thai Buddhist calendar. Month numbers, month codes, and days are identical to ISO 8601, with one era and an epoch year differing from the epoch year in *"gregory"*. All years are treated as having 12 months. For compatibility with other systems, dates prior to ISO year 1941 (2484 BE) are defined but may not correspond to historically accurate values due to the calendar reforms of 1940 and earlier.
    *"chinese"*Traditional Chinese calendar. Lunisolar calendar, based on data published by the Purple Mountain Observatory for dates between 1900 and 2100 (which complies with GB/T 33661-2017, Calculation and Promulgation of the Chinese Calendar), falling back to an implementation-defined approximation outside that range. The arithmetic year is as in *"gregory"*, and there are no eras.
    *"coptic"*Coptic calendar. Similar solar algorithm to *"ethioaa"* and *"ethiopic"* but with a different epoch year. There is one era, starting in the epoch year.
    *"dangi"*Traditional Korean calendar. Lunisolar calendar, based on data published by the Korea Astronomy and Space Science Institute (KASI) for dates between 1900 and 2050, falling back to an implementation-defined approximation outside that range. The arithmetic year is as in *"gregory"*, and there are no eras.
    *"ethioaa"*Ethiopian calendar, Amete Alem era. Similar solar algorithm to *"coptic"* and *"ethiopic"*, but with a different epoch year. There is one era, starting in the epoch year.
    *"ethiopic"*Ethiopian calendar, Amete Mihret era. Similar solar algorithm to *"coptic"* and *"ethioaa"*, but with a different epoch year. There are two eras, one starting in the epoch year of *"ethioaa"* and one starting in this calendar's epoch year.
    *"ethiopic-amete-alem"*Alias for *"ethioaa"*.
    *"gregory"*Gregorian calendar. Solar calendar almost identical to the ISO 8601 calendar, except that it does not define week numbering and it contains two eras, one before the epoch year and one after.
    *"hebrew"*Hebrew calendar. Civil calendar with Tishrei as the first month of the year. Lunisolar calendar with one leap month inserted after month 5. There is one era.
    *"indian"*Indian national (or Śaka) calendar. Solar calendar with one era.
    *"islamic-civil"*Tabular Hijri calendar with leap years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29 in the 30-year cycle, and civil epoch (Friday July 16, 622 Julian / 0622-07-19 ISO)
    *"islamic-tbla"*Tabular Hijri calendar with leap years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29 in the 30-year cycle, and astronomical epoch (Thursday July 15, 622 Julian / 0622-07-18 ISO)
    *"islamic-umalqura"*Hijri calendar, Umm al-Qura. Lunar calendar using months calculated by King Abdulaziz City for Science and Technology (KACST) for dates from the start of 1300 AH to the end of 1600 AH, falling back to *"islamic-civil"* outside that range.
    *"islamicc"*Alias for *"islamic-civil"*.
    *"iso8601"*ISO 8601 calendar. Fully specified in ECMA-262.
    *"japanese"*Japanese Imperial calendar, era system hybridised with the era system used in *"gregory"*. Month numbers, month codes, and days are the same as in the ISO 8601 calendar. For dates up to and including 1872-12-31, years and eras identical to *"gregory"* are used. For later dates, the eras and ranges defined by the Japanese government are used. Note that ISO year 1873 is the 6th year of the Meiji period, starting on ISO date 1868-10-23, during which calendar reforms took place. The arithmetic year is identical to *"gregory"*.
    *"persian"*Persian (or Solar Hijri) calendar. There is one era. Solar calendar using leap years as published by the Iranian calendar authority for dates between 1206 AP and 1498 AP, falling back to an implementation-defined approximation outside that range.
    *"roc"*Republic of China (or Minguo) calendar. Month numbers, month codes, and days are the same as in the ISO 8601 calendar, with two eras, one before the epoch year and one after. The epoch year is different from *"gregory"*.
    +
    +
    + + +

    AvailableCalendars ( ): a List of calendar types

    +
    +
    description
    +
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both *"islamicc"* and *"islamic-civil"*). The List must consist of the "Calendar Type" value of every row of , except the header row.
    +
    +

    This definition supersedes the definition provided in .

    +
    +

    Pattern String Types

    Pattern String is a String value which contains zero or more substrings of the form *"{key}"*, where key can be any non-empty sequence consisting only of elements from the ASCII word characters. The syntax of the abstract pattern strings is an implementation detail and is not exposed to users of ECMA-402.

    diff --git a/spec/negotiation.html b/spec/negotiation.html index 353e6d1e..773c231f 100644 --- a/spec/negotiation.html +++ b/spec/negotiation.html @@ -28,8 +28,8 @@

    Internal slots of Service Constructors

    For example, an implementation of DateTimeFormat might include the language tag *"fa-IR"* in its [[AvailableLocales]] internal slot, and must (according to ) include the keys *"ca"*, *"hc"*, and *"nu"* in its [[RelevantExtensionKeys]] internal slot. - The default calendar for that locale is usually *"persian"*, but an implementation might also support *"gregory"*, *"islamic"*, and *"islamic-civil"*. - The Record in the DateTimeFormat [[LocaleData]] internal slot would therefore include a [[fa-IR]] field whose value is a Record like { [[ca]]: « *"persian"*, *"gregory"*, *"islamic"*, *"islamic-civil"* », [[hc]]: « … », [[nu]]: « … » }, along with other locale-named fields having the same value shape but different elements in their Lists. + The default calendar for that locale is usually *"persian"*, but an implementation might also support *"gregory"* and *"islamic-civil"*. + The Record in the DateTimeFormat [[LocaleData]] internal slot would therefore include a [[fa-IR]] field whose value is a Record like { [[ca]]: « *"persian"*, *"gregory"*, *"islamic-civil"* », [[hc]]: « … », [[nu]]: « … » }, along with other locale-named fields having the same value shape but different elements in their Lists.
    diff --git a/spec/normative-references.html b/spec/normative-references.html index 6ccc8546..e4a0f1f9 100644 --- a/spec/normative-references.html +++ b/spec/normative-references.html @@ -46,6 +46,9 @@

    Normative References

  • Part 3 Numbers, Section 5.1.1 Operands
  • +
  • + Part 4 Dates, Calendar Data +
  • From 9d19c9aa416bfa8ede7f4a4d4936923437575c17 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 25 Feb 2026 10:38:17 -0800 Subject: [PATCH 04/17] (fixup) Reinstate GetOption The discussion in the ECMA-262 editor call settled on not having the same GetOption operation as in ECMA-402, so this can stay. --- spec/negotiation.html | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/spec/negotiation.html b/spec/negotiation.html index 773c231f..baaed9a4 100644 --- a/spec/negotiation.html +++ b/spec/negotiation.html @@ -347,6 +347,35 @@

    + +

    + GetOption ( + _options_: an Object, + _property_: a property key, + _type_: ~boolean~ or ~string~, + _values_: ~empty~ or a List of ECMAScript language values, + _default_: ~required~ or an ECMAScript language value, + ): either a normal completion containing an ECMAScript language value or a throw completion +

    +
    +
    description
    +
    It extracts the value of the specified property of _options_, converts it to the required _type_, checks whether it is allowed by _values_ if _values_ is not ~empty~, and substitutes _default_ if the value is *undefined*.
    +
    + + 1. Let _value_ be ? Get(_options_, _property_). + 1. If _value_ is *undefined*, then + 1. If _default_ is ~required~, throw a *RangeError* exception. + 1. Return _default_. + 1. If _type_ is ~boolean~, then + 1. Set _value_ to ToBoolean(_value_). + 1. Else, + 1. Assert: _type_ is ~string~. + 1. Set _value_ to ? ToString(_value_). + 1. If _values_ is not ~empty~ and _values_ does not contain _value_, throw a *RangeError* exception. + 1. Return _value_. + +
    +

    GetBooleanOrStringNumberFormatOption ( From 4cf4fac0c36f702238198bb2eb48dcc5f16545a5 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 25 Feb 2026 11:13:43 -0800 Subject: [PATCH 05/17] (optional) Reinstate CanonicalizeUValue and override CanonicalizeCalendar See the commit named "(optional) Change approach for specifying CanonicalizeCalendar" in https://github.com/tc39/ecma262/pull/3759 - pending discussion with the ECMA-262 editors. --- spec/locales-currencies-tz.html | 20 ++++++++++++++++++++ spec/negotiation.html | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index 34475f0e..dd7b6425 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -626,6 +626,26 @@

    AvailableCalendars ( ): a List of calendar types

    This definition supersedes the definition provided in .

    + +

    + CanonicalizeCalendar ( + _id_: a String, + ): either a normal completion containing a calendar type, or a throw completion +

    +
    +
    description
    +
    It returns the canonical form of the calendar type denoted by _id_, or throws an exception if there is no such built-in calendar.
    +
    +

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + + 1. Let _calendars_ be AvailableCalendars(). + 1. If _calendars_ does not contain the ASCII-lowercase of _id_, throw a *RangeError* exception. + 1. Return CanonicalizeUValue(*"ca"*, _id_). + +
    +

    Pattern String Types

    Pattern String is a String value which contains zero or more substrings of the form *"{key}"*, where key can be any non-empty sequence consisting only of elements from the ASCII word characters. The syntax of the abstract pattern strings is an implementation detail and is not exposed to users of ECMA-402.

    diff --git a/spec/negotiation.html b/spec/negotiation.html index baaed9a4..e7b6898d 100644 --- a/spec/negotiation.html +++ b/spec/negotiation.html @@ -80,6 +80,25 @@

    + +

    + CanonicalizeUValue ( + _ukey_: a Unicode locale extension sequence key defined in Unicode Technical Standard #35 Part 1 Core Section 3.6.1 Key and Type Definitions, + _uvalue_: a String, + ): a String +

    +
    +
    description
    +
    The returned String is the canonical and case-regularized form of _uvalue_ as a value of _ukey_.
    +
    + + 1. Let _lowerValue_ be the ASCII-lowercase of _uvalue_. + 1. Let _canonicalized_ be the String value resulting from canonicalizing _lowerValue_ as a value of key _ukey_ per Unicode Technical Standard #35 Part 1 Core, Annex C LocaleId Canonicalization Section 5 Canonicalizing Syntax, Processing LocaleIds. + 1. NOTE: It is recommended that implementations use the 'u' extension data in common/bcp47 provided by the Common Locale Data Repository (available at https://cldr.unicode.org/). + 1. Return _canonicalized_. + +
    +

    LookupMatchingLocaleByPrefix ( From 0f2a5e699eabfbb203df877a535d672c21c53c09 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 25 Feb 2026 11:19:12 -0800 Subject: [PATCH 06/17] (fixup) Fix assertion failure in NonISOCalendarDateToISO https://github.com/tc39/proposal-intl-era-monthcode/pull/122 --- spec/locale-sensitive-functions.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index 079ad46a..c7930373 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -1423,13 +1423,19 @@

    1. Assert: _fields_.[[Year]], _fields_.[[Month]], and _fields_.[[Day]] are not ~unset~. 1. If _fields_.[[MonthCode]] is not ~unset~, then 1. Perform ? ConstrainMonthCode(_calendar_, _fields_.[[Year]], _fields_.[[MonthCode]], _overflow_). - 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _fields_.[[Month]]). + 1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _fields_.[[Year]]). + 1. If _fields_.[[Month]] > _monthsInYear_, then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _month_ be _monthsInYear_. + 1. Else, + 1. Let _month_ be _fields_.[[Month]]. + 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _month_). 1. If _fields_.[[Day]] > _daysInMonth_, then 1. If _overflow_ is ~reject~, throw a *RangeError* exception. 1. Let _day_ be _daysInMonth_. 1. Else, 1. Let _day_ be _fields_.[[Day]]. - 1. Return ? CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). + 1. Return ? CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _month_, _day_). From 403dca8b2c6072eb7d32c92ff6865cbd951674d1 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 26 Feb 2026 11:48:58 -0800 Subject: [PATCH 07/17] (fixup) Express epoch nanoseconds in mathematical values See the commit named "(review) Express epoch nanoseconds in mathematical values" in https://github.com/tc39/ecma262/pull/3759 - this change is based on feedback from the ECMA-262 editors that epoch time should be specified as mathematical values. --- spec/datetimeformat.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index 74788d11..adcfaeb7 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -1447,7 +1447,7 @@

    _dateTimeFormat_: an Intl.DateTimeFormat, _format_: a DateTime Format Record or a DateTime Range Pattern Format Record, _pattern_: a Pattern String, - _epochNanoseconds_: a BigInt, + _epochNanoseconds_: an epoch nanoseconds value, _isPlain_: a Boolean, ): a List of Records with fields [[Type]] (a String) and [[Value]] (a String)

    @@ -1815,7 +1815,7 @@

    Value Format Records

    [[EpochNanoseconds]] - a BigInt + an epoch nanoseconds value [[IsPlain]] @@ -1969,7 +1969,7 @@

    1. Set _x_ to TimeClip(_x_). 1. If _x_ is *NaN*, throw a *RangeError* exception. - 1. Let _epochNanoseconds_ be ℤ(ℝ(_x_) × 106). + 1. Let _epochNanoseconds_ be ℝ(_x_) × 106. 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. 1. Return Value Format Record { [[Format]]: _format_, @@ -2004,7 +2004,7 @@

    ToLocalTime ( - _epochNs_: a BigInt, + _epochNs_: an epoch nanoseconds value, _calendar_: a String, _timeZoneIdentifier_: a String, ): a ToLocalTime Record @@ -2021,7 +2021,7 @@

    1. Else, 1. Assert: GetAvailableNamedTimeZoneIdentifier(_timeZoneIdentifier_) is not ~empty~. 1. Let _offsetNs_ be GetNamedTimeZoneOffsetNanoseconds(_timeZoneIdentifier_, _epochNs_). - 1. Let _tz_ be ℝ(_epochNs_) + _offsetNs_. + 1. Let _tz_ be _epochNs_ + _offsetNs_. 1. If _calendar_ is *"gregory"*, then 1. Return a ToLocalTime Record with fields calculated from _tz_ according to . 1. Else, From 9e1b06726809e4d1ed51cddbd80c9326e3486f89 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 5 Mar 2026 15:53:14 -0800 Subject: [PATCH 08/17] (fixup) Assertions in NonISOResolveFields/NonISOMonthDay See https://github.com/tc39/proposal-intl-era-monthcode/pull/124 h/t fabon-f --- spec/locale-sensitive-functions.html | 63 +++++++++++++--------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index c7930373..19eadcd6 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -1463,44 +1463,38 @@

    This definition supersedes the definition provided in .

    It performs the following steps when called:

    - 1. Assert: _fields_.[[Month]] and _fields_.[[Day]] are not ~unset~. - 1. Assert: If _fields_.[[MonthCode]] is ~unset~, _fields_.[[Year]] is not ~unset~. - 1. Let _monthCode_ be _fields_.[[MonthCode]]. - 1. If _calendar_ is *"chinese"* or *"dangi"*, then - 1. NOTE: This special case handles combinations of month and day that theoretically could occur but are not known to have occurred historically and cannot be accurately calculated to occur in the future, even if it may be possible to construct a PlainDate with such combinations due to inaccurate approximations. This is explicitly mentioned here because as time goes on, these dates may become known to have occurred historically, or may be more accurately calculated to occur in the future. - 1. Let _daysInMonth_ be 30. - 1. If _fields_.[[Day]] > _daysInMonth_, then + 1. Assert: _fields_.[[Day]] is not ~unset~. + 1. If _fields_.[[Year]] is not ~unset~, then + 1. Assert: _fields_.[[Month]] is not ~unset~. + 1. [declared="isoDate"] If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. + 1. NOTE: The above step exists so as not to require calculating whether the month and day described in _fields_ exist in user-provided years arbitrarily far in the future or past. + 1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _fields_.[[Year]]). + 1. If _fields_.[[Month]] > _monthsInYear_, then 1. If _overflow_ is ~reject~, throw a *RangeError* exception. - 1. Let _day_ be _daysInMonth_. + 1. Let _month_ be _monthsInYear_. 1. Else, - 1. Let _day_ be _fields_.[[Day]]. - 1. If _fields_.[[Year]] is not ~unset~, then - 1. [declared="isoDate"] If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. - 1. NOTE: The above step exists so as not to require calculating whether the month and day described in _fields_ exist in user-provided years arbitrarily far in the future or past. - 1. If _monthCode_ is ~unset~, then - 1. Let _fieldsISODate_ be ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). - 1. Set _monthCode_ to NonISOCalendarISOToDate(_calendar_, _fieldsISODate_).[[MonthCode]]. + 1. Let _month_ be _fields_.[[Month]]. + 1. If _fields_.[[MonthCode]] is ~unset~, then + 1. Let _fieldsISODate_ be ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _month_, 1). + 1. Let _monthCode_ be NonISOCalendarISOToDate(_calendar_, _fieldsISODate_).[[MonthCode]]. + 1. Else, + 1. Let _monthCode_ be ? ConstrainMonthCode(_calendar_, _fields_.[[Year]], _fields_.[[MonthCode]], _overflow_). + 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _month_). + 1. Else, + 1. Assert: _fields_.[[MonthCode]] is not ~unset~. + 1. Let _monthCode_ be _fields_.[[MonthCode]]. + 1. If _calendar_ is *"chinese"* or *"dangi"*, let _daysInMonth_ be 30; else, let _daysInMonth_ be the maximum number of days in the month described by _monthCode_ in any year. + 1. If _fields_.[[Day]] > _daysInMonth_, then + 1. If _overflow_ is ~reject~, throw a *RangeError* exception. + 1. Let _day_ be _daysInMonth_. + 1. Else, + 1. Let _day_ be _fields_.[[Day]]. + 1. If _calendar_ is *"chinese"* or *"dangi"*, then + 1. NOTE: This special case handles combinations of month and day that theoretically could occur but are not known to have occurred historically and cannot be accurately calculated to occur in the future, even if it may be possible to construct a PlainDate with such combinations due to inaccurate approximations. This is explicitly mentioned here because as time goes on, these dates may become known to have occurred historically, or may be more accurately calculated to occur in the future. 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_. 1. If the "Reference Year (Days 1-29)" column of _row_ is "—", or _day_ = 30 and the "Reference Year (Day 30)" column of _row_ is "—", then 1. If _overflow_ is ~reject~, throw a *RangeError* exception. 1. Set _monthCode_ to CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). - 1. Else, - 1. If _fields_.[[Year]] is not ~unset~, then - 1. If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. - 1. If _monthCode_ is not ~unset~, then - 1. Set _monthCode_ to ? ConstrainMonthCode(_calendar_, _fields_.[[Year]], _monthCode_, _overflow_). - 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _fields_.[[Year]], _fields_.[[Month]]). - 1. Else, - 1. Assert: _monthCode_ is not ~unset~. - 1. Let _daysInMonth_ be the maximum number of days in the month described by _monthCode_ in any year. - 1. If _fields_.[[Day]] > _daysInMonth_, then - 1. If _overflow_ is ~reject~, throw a *RangeError* exception. - 1. Let _day_ be _daysInMonth_. - 1. Else, - 1. Let _day_ be _fields_.[[Day]]. - 1. If _monthCode_ is ~unset~, then - 1. Let _fieldsISODate_ be ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], _fields_.[[Month]], _day_). - 1. Set _monthCode_ to NonISOCalendarISOToDate(_calendar_, _fieldsISODate_).[[MonthCode]]. 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_ (or "any" or "others" if there is no row matching _monthCode_ exactly). 1. Assert: _monthCode_ and _day_ do not match an entry in _row_ that is "—". 1. Let _referenceYear_ be the ISO reference year for _monthCode_ and _day_ as described in _row_. @@ -1811,6 +1805,9 @@

    1. If _type_ is ~date~ or _type_ is ~year-month~, set _needsYear_ to *true*. 1. If _fields_.[[MonthCode]] is ~unset~, set _needsYear_ to *true*. 1. If _fields_.[[Month]] is not ~unset~, set _needsYear_ to *true*. + 1. Let _needsOrdinalMonth_ be *false*. + 1. If _fields_.[[Year]] is not ~unset~, set _needsOrdinalMonth_ to *true*. + 1. If _fields_.[[EraYear]] is not ~unset~, set _needsOrdinalMonth_ to *true*. 1. Let _needsDay_ be *false*. 1. If _type_ is ~date~ or _type_ is ~month-day~, set _needsDay_ to *true*. 1. If _needsYear_ is *true*, then @@ -1841,7 +1838,7 @@

    1. NOTE: _fields_.[[MonthCode]] is intentionally not overwritten with _constrainedMonthCode_. Pending the "overflow" parameter in CalendarDateToISO or CalendarMonthDayToISOReferenceDate, a month code not occurring in _fields_.[[Year]] may cause that operation to throw. However, if _fields_.[[Month]] is present, it must agree with the constrained month code. 1. Assert: _fields_.[[Era]] and _fields_.[[EraYear]] are ~unset~. 1. Assert: If _needsYear_ is *true*, _fields_.[[Year]] is not ~unset~. - 1. Assert: _fields_.[[Month]] is not ~unset~. + 1. Assert: If _needsOrdinalMonth_ is *true*, _fields_.[[Month]] is not ~unset~. 1. Assert: If _needsDay_ is *true*, _fields_.[[Day]] is not ~unset~. 1. Return ~unused~. From 4db49a41a173d260cf34f90192658c00c531951f Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 9 Mar 2026 20:27:25 -0700 Subject: [PATCH 09/17] (fixup) Fix cut-off sentence See https://github.com/tc39/proposal-intl-era-monthcode/pull/126 h/t Tim Flynn --- spec/locale-sensitive-functions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index 19eadcd6..0928159f 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -1774,7 +1774,7 @@

    1. Let _ignoredKeys_ be a copy of _keys_. 1. For each element _key_ of _keys_, do 1. If _key_ is ~month~, append ~month-code~ to _ignoredKeys_. - 1. If _key_ is ~month-code~, append ~month~. + 1. If _key_ is ~month-code~, append ~month~ to _ignoredKeys_. 1. If _key_ is one of ~era~, ~era-year~, or ~year~ and CalendarSupportsEra(_calendar_) is *true*, then 1. Append ~era~, ~era-year~, and ~year~ to _ignoredKeys_. 1. If _key_ is one of ~day~, ~month~, or ~month-code~ and CalendarHasMidYearEras(_calendar_) is *true*, then From 2fff84b63d5b13a7da7dd6423d88e5cbc51672e5 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 10 Mar 2026 08:32:47 -0700 Subject: [PATCH 10/17] (fixup) Clarify early throw in NonISOMonthDayToISOReferenceDate This adjusts the language in the step in NonISOMonthDayToISOReferenceDate, clarifying that implementations must eventually use the month and day results but don't have to calculate them if the year is already out of range. h/t fabon-f See https://github.com/tc39/proposal-intl-era-monthcode/issues/125 --- spec/locale-sensitive-functions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index 0928159f..afe9c87d 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -1466,7 +1466,7 @@

    1. Assert: _fields_.[[Day]] is not ~unset~. 1. If _fields_.[[Year]] is not ~unset~, then 1. Assert: _fields_.[[Month]] is not ~unset~. - 1. [declared="isoDate"] If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. + 1. [declared="isoDate"] If there exists no combination of inputs such that ! CalendarIntegersToISO(_calendar_, _fields_.[[Year]], ..., ...) with the constrained _fields_.[[Month]] and _fields_.[[Day]] would return an ISO Date Record _isoDate_ for which ISODateWithinLimits(_isoDate_) is *true*, throw a *RangeError* exception. 1. NOTE: The above step exists so as not to require calculating whether the month and day described in _fields_ exist in user-provided years arbitrarily far in the future or past. 1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _fields_.[[Year]]). 1. If _fields_.[[Month]] > _monthsInYear_, then From 372edafd0ae64a802fe30ce9c10c848b06c01e18 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 11:21:37 -0400 Subject: [PATCH 11/17] (fixup) Use curly quotes for table column names Review feedback from Richard --- spec/datetimeformat.html | 4 +- spec/durationformat.html | 8 ++-- spec/locale-sensitive-functions.html | 64 ++++++++++++++-------------- spec/locales-currencies-tz.html | 2 +- spec/numberformat.html | 6 +-- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index adcfaeb7..6425ed46 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -1313,7 +1313,7 @@

    _formats_: a List of DateTime Format Records, _baseFormat_: a DateTime Format Record, _matcher_: either *"basic"* or *"best fit"*, - _allowedOptions_: a List of property names from the "Property" column of , + _allowedOptions_: a List of property names from the “Property” column of , ): a DateTime Format Record

    @@ -1327,7 +1327,7 @@

    1. Let _anyConflictingFields_ be *false*. 1. For each row of , except the header row, in table order, do - 1. Let _prop_ be the name given in the "Property" column of the current row. + 1. Let _prop_ be the name given in the “Property” column of the current row. 1. If _baseFormat_ has a [[<_prop_>]] field and _allowedOptions_ does not contain _prop_, set _anyConflictingFields_ to true. 1. If _anyConflictingFields_ is *false*, return _baseFormat_. 1. NOTE: The above steps prevent the operation from returning an altered format when _baseFormat_ would be sufficient. This should be unnecessary, but exists because the ECMA-402 specification does not guarantee that a format returned from DateTimeStyleFormat can also be returned from BasicFormatMatcher or BestFitFormatMatcher. diff --git a/spec/durationformat.html b/spec/durationformat.html index 59f58d41..f68dc0e5 100644 --- a/spec/durationformat.html +++ b/spec/durationformat.html @@ -488,11 +488,11 @@

    1. Let _result_ be 0. 1. Let _exponent_ be 3. 1. For each row of , except the header row, in table order, do - 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the "Formatter Internal Slot" value of the current row. + 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the “Formatter Internal Slot” value of the current row. 1. If _unitOptions_.[[Style]] is *"fractional"*, then 1. Let _unit_ be the Unit value of the current row. 1. Assert: IsFractionalSecondUnitName(_unit_) is *true*. - 1. Let _value_ be the value of _duration_'s internal slot whose name is the "Duration Internal Slot" value of the current row. + 1. Let _value_ be the value of _duration_'s internal slot whose name is the “Duration Internal Slot” value of the current row. 1. Set _result_ to _result_ + (_value_ / 10_exponent_). 1. Set _exponent_ to _exponent_ + 3. 1. Return _result_. @@ -768,8 +768,8 @@

    1. Let _signDisplayed_ be *true*. 1. Let _numericUnitFound_ be *false*. 1. While _numericUnitFound_ is *false*, repeat for each row in in table order, except the header row: - 1. Let _value_ be the value of _duration_'s internal slot whose name is the "Duration Internal Slot" value of the current row. - 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the "Formatter Internal Slot" value of the current row. + 1. Let _value_ be the value of _duration_'s internal slot whose name is the “Duration Internal Slot” value of the current row. + 1. Let _unitOptions_ be the value of _durationFormat_'s internal slot whose name is the “Formatter Internal Slot” value of the current row. 1. Let _style_ be _unitOptions_.[[Style]]. 1. Let _display_ be _unitOptions_.[[Display]]. 1. Let _unit_ be the Unit value of the current row. diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index afe9c87d..2ae2e0ca 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -398,8 +398,8 @@

    - 1. If _calendar_ is listed in the "Calendar" column of , return *true*. - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. If _calendar_ is listed in the “Calendar” column of , return *true*. + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . 1. Return *false*.

    @@ -671,13 +671,13 @@

    1. For each row of , except the header row, do - 1. Let _cal_ be the Calendar value of the current row. + 1. Let _cal_ be the “Calendar” value of the current row. 1. If _cal_ is equal to _calendar_, then - 1. Let _canonicalName_ be the Era value of the current row. + 1. Let _canonicalName_ be the “Era” value of the current row. 1. If _canonicalName_ is equal to _era_, return _canonicalName_. - 1. Let _aliases_ be a List whose elements are the strings given in the "Aliases" column of the row. + 1. Let _aliases_ be a List whose elements are the strings given in the “Aliases” column of the row. 1. If _aliases_ contains _era_, return _canonicalName_. - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . 1. Return *undefined*. @@ -694,7 +694,7 @@

    1. If _calendar_ is *"japanese"*, return *true*. - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . 1. Return *false*. @@ -711,12 +711,12 @@

    1. Let _commonMonthCodes_ be « *"M01"*, *"M02"*, *"M03"*, *"M04"*, *"M05"*, *"M06"*, *"M07"*, *"M08"*, *"M09"*, *"M10"*, *"M11"*, *"M12"* ». 1. If _commonMonthCodes_ contains _monthCode_, return *true*. - 1. If _calendar_ is listed in the "Calendar" column of , then - 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. - 1. Let _specialMonthCodes_ be a List whose elements are the strings given in the "Additional Month Codes" column of _r_. + 1. If _calendar_ is listed in the “Calendar” column of , then + 1. Let _r_ be the row in with a value in the “Calendar” column matching _calendar_. + 1. Let _specialMonthCodes_ be a List whose elements are the strings given in the “Additional Month Codes” column of _r_. 1. If _specialMonthCodes_ contains _monthCode_, return *true*. 1. Return *false*. - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . 1. Return *false*. @@ -806,9 +806,9 @@

    1. Assert: IsValidMonthCodeForCalendar(_calendar_, _monthCode_) is *true*. 1. If YearContainsMonthCode(_calendar_, _arithmeticYear_, _monthCode_) is *true*, return _monthCode_. 1. If _overflow_ is ~reject~, throw a *RangeError* exception. - 1. Assert: _calendar_ is listed in the "Calendar" column of . - 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. - 1. Let _shiftType_ be the value given in the "Leap to Common Month Transformation" column of _r_. + 1. Assert: _calendar_ is listed in the “Calendar” column of . + 1. Let _r_ be the row in with a value in the “Calendar” column matching _calendar_. + 1. Let _shiftType_ be the value given in the “Leap to Common Month Transformation” column of _r_. 1. If _shiftType_ is ~skip-backward~, then 1. Return CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). 1. Else, @@ -837,10 +837,10 @@

    1. Let _monthsBefore_ be 0. 1. Let _number_ be 1. 1. Let _isLeap_ be *false*. - 1. Let _r_ be the row in which the _calendar_ is in the Calendar column. - 1. If the "Leap to Common Month Transformation" column of _r_ is empty, then + 1. Let _r_ be the row in which the _calendar_ is in the “Calendar” column. + 1. If the “Leap to Common Month Transformation” column of _r_ is empty, then 1. Return ! ParseMonthCode(_monthCode_).[[MonthNumber]]. - 1. Assert: The "Additional Month Codes" column of _r_ does not contain *"M00L"* or *"M13"*. + 1. Assert: The “Additional Month Codes” column of _r_ does not contain *"M00L"* or *"M13"*. 1. Assert: This algorithm will return before the following loop terminates by failing its condition. 1. Repeat, while _number_ ≤ 12, 1. Let _currentMonthCode_ be CreateMonthCode(_number_, _isLeap_). @@ -925,9 +925,9 @@

    It performs implementation-defined processing to find the arithmetic year for the date corresponding to _date_ in the context of the calendar represented by _calendar_.

    - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . - 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_. - 1. Let _epochYear_ be the value given in the "Epoch ISO Year" column of _r_. + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . + 1. Let _r_ be the row in with a value in the “Calendar” column matching _calendar_. + 1. Let _epochYear_ be the value given in the “Epoch ISO Year” column of _r_. 1. Let _epochDate_ be the first day of the calendar year starting in ISO year _epochYear_ in the calendar represented by _calendar_, according to implementation-defined processing. 1. Let _newYear_ be the first day of the calendar year of _date_ in the calendar represented by _calendar_, according to implementation-defined processing. 1. Let _arithmeticYear_ be the implementation-defined signed number of whole years between _epochDate_ and _newYear_ in the calendar represented by _calendar_. @@ -1022,10 +1022,10 @@

    1. Let _era_ be CanonicalizeEraInCalendar(_calendar_, _era_). 1. Assert: _era_ is not *undefined*. - 1. Assert: _calendar_ is listed in the "Calendar Type" column of . - 1. Let _r_ be the row in with a value in the Calendar column matching _calendar_ and a value in the Era column matching _era_. - 1. Let _eraKind_ be the value given in the "Era Kind" column of _r_. - 1. Let _offset_ be the value given in the "Offset" column of _r_. + 1. Assert: _calendar_ is listed in the “Calendar Type” column of . + 1. Let _r_ be the row in with a value in the “Calendar” column matching _calendar_ and a value in the “Era” column matching _era_. + 1. Let _eraKind_ be the value given in the “Era Kind” column of _r_. + 1. Let _offset_ be the value given in the “Offset” column of _r_. 1. If _eraKind_ is ~epoch~, return _eraYear_. 1. If _eraKind_ is ~negative~, return 1 - _eraYear_. 1. Assert: _eraKind_ is ~offset~. @@ -1491,12 +1491,12 @@

    1. Let _day_ be _fields_.[[Day]]. 1. If _calendar_ is *"chinese"* or *"dangi"*, then 1. NOTE: This special case handles combinations of month and day that theoretically could occur but are not known to have occurred historically and cannot be accurately calculated to occur in the future, even if it may be possible to construct a PlainDate with such combinations due to inaccurate approximations. This is explicitly mentioned here because as time goes on, these dates may become known to have occurred historically, or may be more accurately calculated to occur in the future. - 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_. - 1. If the "Reference Year (Days 1-29)" column of _row_ is "—", or _day_ = 30 and the "Reference Year (Day 30)" column of _row_ is "—", then + 1. Let _row_ be the row in with a value matching _calendar_ in the “Calendar Type” column and a value in the “Month Code” column matching _monthCode_. + 1. If the “Reference Year (Days 1-29)” column of _row_ is “—”, or _day_ = 30 and the “Reference Year (Day 30)” column of _row_ is “—”, then 1. If _overflow_ is ~reject~, throw a *RangeError* exception. 1. Set _monthCode_ to CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). - 1. Let _row_ be the row in with a value matching _calendar_ in the "Calendar Type" column and a value in the "Month Code" column matching _monthCode_ (or "any" or "others" if there is no row matching _monthCode_ exactly). - 1. Assert: _monthCode_ and _day_ do not match an entry in _row_ that is "—". + 1. Let _row_ be the row in with a value matching _calendar_ in the “Calendar Type” column and a value in the “Month Code” column matching _monthCode_ (or “any” or “others” if there is no row matching _monthCode_ exactly). + 1. Assert: _monthCode_ and _day_ do not match an entry in _row_ that is “—”. 1. Let _referenceYear_ be the ISO reference year for _monthCode_ and _day_ as described in _row_. 1. Return the latest possible ISO Date Record _isoDate_ such that _isoDate_.[[Year]] = _referenceYear_ and NonISOCalendarISOToDate(_calendar_, _isoDate_) returns a Calendar Date Record whose [[MonthCode]] and [[Day]] field values respectively equal _monthCode_ and _day_. @@ -1738,8 +1738,8 @@

    CalendarExtraFields ( _calendar_: a calendar type, - _fields_: a List of values from the "Enumeration Key" column of , - ): a List of values from the "Enumeration Key" column of + _fields_: a List of values from the “Enumeration Key” column of , + ): a List of values from the “Enumeration Key” column of

    description
    @@ -1758,8 +1758,8 @@

    NonISOFieldKeysToIgnore ( _calendar_: a calendar type that is not *"iso8601"*, - _keys_: a List of values from the "Enumeration Key" column of , - ): a List of values from the "Enumeration Key" column of + _keys_: a List of values from the “Enumeration Key” column of , + ): a List of values from the “Enumeration Key” column of

    description
    diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index dd7b6425..e2568dae 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -621,7 +621,7 @@

    Calendar Types

    AvailableCalendars ( ): a List of calendar types

    description
    -
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both *"islamicc"* and *"islamic-civil"*). The List must consist of the "Calendar Type" value of every row of , except the header row.
    +
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both *"islamicc"* and *"islamic-civil"*). The List must consist of the “Calendar Type” value of every row of , except the header row.

    This definition supersedes the definition provided in .

    diff --git a/spec/numberformat.html b/spec/numberformat.html index bb518293..2a2fbc8e 100644 --- a/spec/numberformat.html +++ b/spec/numberformat.html @@ -535,7 +535,7 @@

    Properties of Intl.NumberFormat Instances

    In scientific notation, this slot affects the sign display of the mantissa but not the exponent.
  • [[RoundingIncrement]] is an integer that evenly divides 10, 100, 1000, or 10000 into tenths, fifths, quarters, or halves. It indicates the increment at which rounding should take place relative to the calculated rounding magnitude. For example, if [[MaximumFractionDigits]] is 2 and [[RoundingIncrement]] is 5, then formatted numbers are rounded to the nearest 0.05 ("nickel rounding").
  • -
  • [[RoundingMode]] is a rounding mode, one of the Strings in the "String Identifier" column of .
  • +
  • [[RoundingMode]] is a rounding mode, one of the Strings in the “String Identifier” column of .
  • [[TrailingZeroDisplay]] is one of the String values *"auto"* or *"stripIfInteger"*, indicating whether to strip trailing zeros if the formatted number is an integer (i.e., has no non-zero fraction digit).
  • @@ -1181,7 +1181,7 @@

    _x_: a non-negative mathematical value, _minPrecision_: an integer in the inclusive interval from 1 to 21, _maxPrecision_: an integer in the inclusive interval from 1 to 21, - _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, + _unsignedRoundingMode_: a specification type from the “Unsigned Rounding Mode” column of , or *undefined*, ): a Record with fields [[FormattedString]] (a String), [[RoundedNumber]] (a mathematical value), [[IntegerDigitsCount]] (an integer), and [[RoundingMagnitude]] (an integer)

    @@ -1239,7 +1239,7 @@

    _minFraction_: an integer in the inclusive interval from 0 to 100, _maxFraction_: an integer in the inclusive interval from 0 to 100, _roundingIncrement_: an integer, - _unsignedRoundingMode_: a specification type from the Unsigned Rounding Mode column of , or *undefined*, + _unsignedRoundingMode_: a specification type from the “Unsigned Rounding Mode” column of , or *undefined*, ): a Record with fields [[FormattedString]] (a String), [[RoundedNumber]] (a mathematical value), [[IntegerDigitsCount]] (an integer), and [[RoundingMagnitude]] (an integer)

    From 19d63188fc9ffbcba717e2d8a8c8927710172274 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 11:30:03 -0400 Subject: [PATCH 12/17] (fixup) Make explicit algorithm steps for AvailableCalendars Review suggestion from Richard. --- spec/locales-currencies-tz.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index e2568dae..ccb85841 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -621,9 +621,18 @@

    Calendar Types

    AvailableCalendars ( ): a List of calendar types

    description
    -
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both *"islamicc"* and *"islamic-civil"*). The List must consist of the “Calendar Type” value of every row of , except the header row.
    +
    The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form () identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases.

    This definition supersedes the definition provided in .

    +

    It performs the following steps when called:

    + + 1. Let _identifiers_ be a new empty List. + 1. For each row of , except the header row, in table order, do + 1. Let _identifier_ be the “Calendar Type” value of the current row. + 1. Append _identifier_ to _identifiers_. + 1. Sort _identifiers_ according to lexicographic code unit order. + 1. Return _identifiers_. + From ac5929d9e7b166c4b27dc421d4f33420229f697f Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 12:55:57 -0400 Subject: [PATCH 13/17] (fixup) Improvements to toLocaleStringTimeZone in CreateDateTimeFormat Review suggestions from Richard and Anba. --- spec/datetimeformat.html | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index 6425ed46..3fac0f85 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -59,7 +59,7 @@

    description
    -
    If the additional _toLocaleStringTimeZone_ argument is provided, the time zone will be overridden and some adjustments will be made to the defaults in order to implement the behaviour of `Temporal.ZonedDateTime.prototype.toLocaleString`.
    +
    _toLocaleStringTimeZone_ supports the behaviour of %Temporal.ZonedDateTime.prototype.toLocaleString%.
    @@ -90,14 +90,11 @@

    1. If _hc_ is *null*, set _hc_ to _resolvedLocaleData_.[[hourCycle]]. 1. Set _dateTimeFormat_.[[HourCycle]] to _hc_. 1. Let _timeZone_ be ? Get(_options_, *"timeZone"*). - 1. If _timeZone_ is *undefined*, then - 1. If _toLocaleStringTimeZone_ is present, then - 1. Set _timeZone_ to _toLocaleStringTimeZone_. - 1. Else, - 1. Set _timeZone_ to SystemTimeZoneIdentifier(). + 1. If _toLocaleStringTimeZone_ is present, then + 1. If _timeZone_ is not *undefined*, throw a *TypeError* exception. + 1. Set _timeZone_ to _toLocaleStringTimeZone_. 1. Else, - 1. If _toLocaleStringTimeZone_ is present, throw a *TypeError* exception. - 1. Set _timeZone_ to ? ToString(_timeZone_). + 1. If _timeZone_ is *undefined*, set _timeZone_ to SystemTimeZoneIdentifier(); else, set _timeZone_ to ? ToString(_timeZone_). 1. If IsOffsetTimeZoneIdentifier(_timeZone_) is *true*, then 1. Let _parseResult_ be ParseText(StringToCodePoints(_timeZone_), |UTCOffset[~SubMinutePrecision]|). 1. Assert: _parseResult_ is a Parse Node. From cf8bee79d20e5a7d2e4acdec7b7f85e85615d313 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 14:23:03 -0400 Subject: [PATCH 14/17] (fixup) Return "the" Value Format Record Review feedback from Richard. --- spec/datetimeformat.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index 3fac0f85..8fd6f7a1 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -1837,7 +1837,7 @@

    1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateFormat]]. 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, [[IsPlain]]: *true* @@ -1860,7 +1860,7 @@

    1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainYearMonthFormat]]. 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, [[IsPlain]]: *true* @@ -1883,7 +1883,7 @@

    1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainMonthDayFormat]]. 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, [[IsPlain]]: *true* @@ -1906,7 +1906,7 @@

    1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainTimeFormat]]. 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, [[IsPlain]]: *true* @@ -1927,7 +1927,7 @@

    1. If _dateTime_.[[Calendar]] is not *"iso8601"* and not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. 1. Let _epochNs_ be GetUTCEpochNanoseconds(_dateTime_.[[ISODateTime]]). 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateTimeFormat]]. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, [[IsPlain]]: *true* @@ -1946,7 +1946,7 @@

    1. Let _format_ be _dateTimeFormat_.[[TemporalInstantFormat]]. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _instant_.[[EpochNanoseconds]], [[IsPlain]]: *false* @@ -1968,7 +1968,7 @@

    1. If _x_ is *NaN*, throw a *RangeError* exception. 1. Let _epochNanoseconds_ be ℝ(_x_) × 106. 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. - 1. Return Value Format Record { + 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNanoseconds_, [[IsPlain]]: *false* From ffce532d3b416ad5f0638f7ae1e726136a6ecbc5 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 14:43:17 -0400 Subject: [PATCH 15/17] (fixup) Inline Value Format Record AOs Review suggestion from Richard. --- spec/datetimeformat.html | 201 ++++++++------------------------------- 1 file changed, 42 insertions(+), 159 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index 8fd6f7a1..ef0fa398 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -1822,90 +1822,57 @@

    Value Format Records

    - -

    - PlainDateFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _temporalDate_: a Temporal.PlainDate, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. If _temporalDate_.[[Calendar]] is not either _dateTimeFormat_.[[Calendar]] or *"iso8601"*, throw a *RangeError* exception. - 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalDate_.[[ISODate]], NoonTimeRecord()). - 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). - 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateFormat]]. - 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _epochNs_, - [[IsPlain]]: *true* - }. - -
    - - -

    - PlainYearMonthFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _temporalYearMonth_: a Temporal.PlainYearMonth, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. If _temporalYearMonth_.[[Calendar]] is not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. - 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalYearMonth_.[[ISODate]], NoonTimeRecord()). - 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). - 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainYearMonthFormat]]. - 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _epochNs_, - [[IsPlain]]: *true* - }. - -
    - - +

    - PlainMonthDayFormatRecord ( + ValueFormatRecord ( _dateTimeFormat_: an Intl.DateTimeFormat, - _temporalMonthDay_: a Temporal.PlainMonthDay, + _x_: either a Number, or an Object for which IsTemporalObject returns *true*, ): either a normal completion containing a Value Format Record or a throw completion

    - 1. If _temporalMonthDay_.[[Calendar]] is not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. - 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_temporalMonthDay_.[[ISODate]], NoonTimeRecord()). - 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). - 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainMonthDayFormat]]. + 1. Let _dtfCalendar_ be _dateTimeFormat_.[[Calendar]]. + 1. If _x_ is a Number, then + 1. Let _v_ be TimeClip(_x_). + 1. If _v_ is *NaN*, throw a *RangeError* exception. + 1. Let _epochNanoseconds_ be ℝ(_v_) × 106. + 1. Return the Value Format Record { + [[Format]]: _dateTimeFormat_.[[DateTimeFormat]], + [[EpochNanoseconds]]: _epochNanoseconds_, + [[IsPlain]]: *false* + }. + 1. If _x_ has an [[InitializedTemporalInstant]] internal slot, then + 1. Return the Value Format Record { + [[Format]]: _dateTimeFormat_.[[TemporalInstantFormat]], + [[EpochNanoseconds]]: _x_.[[EpochNanoseconds]], + [[IsPlain]]: *false* + }. + 1. If _x_ has an [[InitializedTemporalDateTime]] internal slot, then + 1. If _x_.[[Calendar]] is not either _dtfCalendar_ or *"iso8601"*, throw a *RangeError* exception. + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateTimeFormat]]. + 1. Let _isoDateTime_ be _x_.[[ISODateTime]]. + 1. Else if _x_ has an [[InitializedTemporalDate]] internal slot, then + 1. If _x_.[[Calendar]] is not either _dtfCalendar_ or *"iso8601"*, throw a *RangeError* exception. + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateFormat]]. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_x_.[[ISODate]], NoonTimeRecord()). + 1. Else if _x_ has an [[InitializedTemporalYearMonth]] internal slot, then + 1. If _x_.[[Calendar]] is not equal to _dtfCalendar_, throw a *RangeError* exception. + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainYearMonthFormat]]. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_x_.[[ISODate]], NoonTimeRecord()). + 1. Else if _x_ has an [[InitializedTemporalMonthDay]] internal slot, then + 1. If _x_.[[Calendar]] is not equal to _dtfCalendar_, throw a *RangeError* exception. + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainMonthDayFormat]]. + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_x_.[[ISODate]], NoonTimeRecord()). + 1. Else if _x_ has an [[InitializedTemporalTime]] internal slot, then + 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainTimeFormat]]. + 1. Let _isoDate_ be CreateISODateRecord(1970, 1, 1). + 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_isoDate_, _x_.[[Time]]). + 1. Else, + 1. Assert: _x_ has an [[InitializedTemporalZonedDateTime]] internal slot. + 1. Throw a *TypeError* exception. 1. If _format_ is *null*, throw a *TypeError* exception. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _epochNs_, - [[IsPlain]]: *true* - }. - -
    - - -

    - PlainTimeFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _temporalTime_: a Temporal.PlainTime, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. Let _isoDate_ be CreateISODateRecord(1970, 1, 1). - 1. Let _isoDateTime_ be CombineISODateAndTimeRecord(_isoDate_, _temporalTime_.[[Time]]). 1. Let _epochNs_ be GetUTCEpochNanoseconds(_isoDateTime_). - 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainTimeFormat]]. - 1. If _format_ is *null*, throw a *TypeError* exception. 1. Return the Value Format Record { [[Format]]: _format_, [[EpochNanoseconds]]: _epochNs_, @@ -1914,90 +1881,6 @@

    - -

    - PlainDateTimeFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _dateTime_: a Temporal.PlainDateTime, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. If _dateTime_.[[Calendar]] is not *"iso8601"* and not equal to _dateTimeFormat_.[[Calendar]], throw a *RangeError* exception. - 1. Let _epochNs_ be GetUTCEpochNanoseconds(_dateTime_.[[ISODateTime]]). - 1. Let _format_ be _dateTimeFormat_.[[TemporalPlainDateTimeFormat]]. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _epochNs_, - [[IsPlain]]: *true* - }. - -
    - - -

    - InstantFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _instant_: a Temporal.Instant, - ): a Value Format Record -

    -
    -
    - - 1. Let _format_ be _dateTimeFormat_.[[TemporalInstantFormat]]. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _instant_.[[EpochNanoseconds]], - [[IsPlain]]: *false* - }. - -
    - - -

    - TimeValueFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: a Number, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. Set _x_ to TimeClip(_x_). - 1. If _x_ is *NaN*, throw a *RangeError* exception. - 1. Let _epochNanoseconds_ be ℝ(_x_) × 106. - 1. Let _format_ be _dateTimeFormat_.[[DateTimeFormat]]. - 1. Return the Value Format Record { - [[Format]]: _format_, - [[EpochNanoseconds]]: _epochNanoseconds_, - [[IsPlain]]: *false* - }. - -
    - - -

    - ValueFormatRecord ( - _dateTimeFormat_: an Intl.DateTimeFormat, - _x_: either a Number, or an Object for which IsTemporalObject returns *true*, - ): either a normal completion containing a Value Format Record or a throw completion -

    -
    -
    - - 1. If _x_ is a Number, return ? TimeValueFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalDate]] internal slot, return ? PlainDateFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalYearMonth]] internal slot, return ? PlainYearMonthFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalMonthDay]] internal slot, return ? PlainMonthDayFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalTime]] internal slot, return ? PlainTimeFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalDateTime]] internal slot, return ? PlainDateTimeFormatRecord(_dateTimeFormat_, _x_). - 1. If _x_ has an [[InitializedTemporalInstant]] internal slot, return InstantFormatRecord(_dateTimeFormat_, _x_). - 1. Assert: _x_ has an [[InitializedTemporalZonedDateTime]] internal slot. - 1. Throw a *TypeError* exception. - -
    -

    ToLocalTime ( From 2bd1d9c0b30c90c056ced202dd4954e5d0b0533c Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 12 Mar 2026 15:02:05 -0400 Subject: [PATCH 16/17] (fixup) Various phrasing adjustments Review suggestions from Richard. --- spec/datetimeformat.html | 6 +++--- spec/locale-sensitive-functions.html | 10 +++++----- spec/locales-currencies-tz.html | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index ef0fa398..f2ee44d1 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -1554,7 +1554,7 @@

    description
    -
    It interprets _x_ as an epoch time, and creates the corresponding parts according to the type of _x_, effective locale, and the formatting options of _dateTimeFormat_.
    +
    It derives an epoch nanoseconds value from _x_ (interpreting _x_ as milliseconds since the epoch if it is a Number), and creates the corresponding parts according to the type of _x_, effective locale, and the formatting options of _dateTimeFormat_.
    1. Let _formatRecord_ be ? ValueFormatRecord(_dateTimeFormat_, _x_). @@ -1620,7 +1620,7 @@

    description
    -
    It interprets _x_ and _y_ as epoch times, and creates the corresponding parts according to the types of _x_ and _y_, effective locale, and the formatting options of _dateTimeFormat_.
    +
    It derives an epoch nanoseconds value from each of _x_ and _y_ (interpreting each Number as milliseconds since the epoch), and creates the corresponding parts according to the types of _x_ and _y_, effective locale, and the formatting options of _dateTimeFormat_.
    1. If IsTemporalObject(_x_) is *true* or IsTemporalObject(_y_) is *true*, then @@ -1798,7 +1798,7 @@

    Value Format Records

    Each Value Format Record has the fields defined in .

    - Record returned by ValueFormatRecord + Value Format Record Fields diff --git a/spec/locale-sensitive-functions.html b/spec/locale-sensitive-functions.html index 2ae2e0ca..3fb10e9e 100644 --- a/spec/locale-sensitive-functions.html +++ b/spec/locale-sensitive-functions.html @@ -812,7 +812,7 @@

    1. If _shiftType_ is ~skip-backward~, then 1. Return CreateMonthCode(! ParseMonthCode(_monthCode_).[[MonthNumber]], *false*). 1. Else, - 1. Assert: _monthCode_ is *"M05L"*. + 1. Assert: _calendar_ is *"hebrew"* and _monthCode_ is *"M05L"*. 1. Return *"M06"*. @@ -877,7 +877,7 @@

    - +

    CalendarDateEra ( _calendar_: a calendar type that is not *"iso8601"*, @@ -895,7 +895,7 @@

    - +

    CalendarDateEraYear ( _calendar_: a calendar type that is not *"iso8601"*, @@ -913,7 +913,7 @@

    - +

    CalendarDateArithmeticYear ( _calendar_: a calendar type that is not *"iso8601"*, @@ -1764,7 +1764,7 @@

    description
    - It performs implementation-defined processing to determine which calendar date fields changing any of the fields named in _keys_ can potentially conflict with or invalidate, for the given _calendar_. + It determines which calendar date fields changing any of the fields named in _keys_ can potentially conflict with or invalidate, for the given _calendar_. A field always invalidates at least itself.
    diff --git a/spec/locales-currencies-tz.html b/spec/locales-currencies-tz.html index ccb85841..c98c0943 100644 --- a/spec/locales-currencies-tz.html +++ b/spec/locales-currencies-tz.html @@ -519,11 +519,11 @@

    AvailableCanonicalCollations ( ): a List of Strings

    Calendar Types

    ECMA-262 describes calendar types, of which *"iso8601"* is required to be supported. - This specification additionally requires ECMAScript implementations to support calendar types corresponding with those of the Unicode Common Locale Data Repository (CLDR). + This specification additionally requires ECMAScript implementations to support an explicit subset of calendar types defined in the Unicode Common Locale Data Repository (CLDR) and prohibits them from supporting other calendar types, although future editions may expand the list.

    - The following table lists the calendar types described in CLDR, along with implementation notes and sources where necessary. + The following table lists the sanctioned calendar types, along with implementation notes and sources where necessary. All calendar types must use a proleptic reckoning, meaning that the current rules for calendrical calculations are extended indefinitely into the past, ignoring dates when historical calendar reforms happened.

    From b44fc546c98f5037240271970670bcc1bdbb9ccf Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Mon, 30 Mar 2026 20:23:33 -0400 Subject: [PATCH 17/17] (fixup) Extra clarification on [[LocaleData]].[[]].[[ca]] Explicitly say why the locale data may include unsupported calendars. --- spec/datetimeformat.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/datetimeformat.html b/spec/datetimeformat.html index f2ee44d1..37791a19 100644 --- a/spec/datetimeformat.html +++ b/spec/datetimeformat.html @@ -74,7 +74,7 @@

    1. Let _r_ be _optionsResolution_.[[ResolvedLocale]]. 1. Set _dateTimeFormat_.[[Locale]] to _r_.[[Locale]]. 1. Let _resolvedCalendar_ be _r_.[[ca]]. - 1. If _resolvedCalendar_ is *"islamic"*, then + 1. [id="step-calendar-fallback"] If _resolvedCalendar_ is *"islamic"*, then 1. Set _resolvedCalendar_ to *"islamic-tbla"*. 1. If the ECMAScript implementation has a mechanism for reporting diagnostic warning messages, a warning should be issued. 1. Set _dateTimeFormat_.[[Calendar]] to _resolvedCalendar_. @@ -211,7 +211,7 @@

    Internal slots

    • [[LocaleData]].[[<_locale_>]].[[ca]] must be a List consisting of calendar types. - It may include calendar types not listed in . + It may include calendar types not listed in , for the purposes of implementing a diagnostic warning message in cases where the locale data references unsupported calendars, as in Step of CreateDateTimeFormat.
    • [[LocaleData]].[[<_locale_>]].[[nu]] must be a List that does not include the values *"native"*, *"traditio"*, or *"finance"*.