Calendars

Calendar Types

At a minimum, ECMAScript implementations must support a built-in calendar named *"iso8601"*, representing the ISO 8601 calendar. In addition, implementations may support any number of other built-in calendars corresponding with those of the Unicode Common Locale Data Repository (CLDR).

ECMAScript implementations identify built-in calendars using a calendar type as defined by Unicode Technical Standard #35, Part 4, Section 2. 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, inclusive) and/or digits (U+0030 DIGIT ZERO through U+0039 DIGIT NINE, inclusive), with zero or more medial hyphens (U+002D HYPHEN-MINUS).

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

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., either both or neither of *"islamicc"* and *"islamic-civil"*). The List must include *"iso8601"*.

Abstract Operations

Calendar Date Records

An 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 .

Field Name Value Meaning
[[Era]] a String or *undefined* A lowercase String value representing the date's era, or *undefined* for calendars that do not have eras.
[[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. Era years are 1-indexed for many calendars, but not all (e.g., the eras of the Burmese calendar 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 year relative to the first day of a calendar-specific "epoch year". The 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"*. 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 relative to the first day of a calendar-specific "epoch 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 calendar system.

More details about this field will be specified in the Intl era and monthCode proposal.

[[DaysInWeek]] a positive integer The number of days in the date's week.
[[DaysInMonth]] a positive integer The number of days in the date's month.
[[DaysInYear]] a positive integer The number of days in the date's year.
[[MonthsInYear]] a positive integer The 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).

Calendar Fields Records

A Calendar Fields Record is a Record value used to represent full or partial input for a calendar date in a non-ISO 8601 calendar. Calendar Fields Records are produced by several abstract operations, such as ISODateToFields and PrepareCalendarFields, and are passed to abstract operations such as CalendarDateFromFields.

Many of the fields in a Calendar Fields Record have the same meaning as the fields of the same name in Calendar Date Records, but each field in a Calendar Fields Record may additionally be ~unset~ to indicate partial input.

Each field has a corresponding property key. These property keys correspond to the properties that are read from user input objects to populate the field, in methods such as `Temporal.PlainDate.prototype.with()`.

Each field has a corresponding enumeration key. Lists of these enumeration keys are passed to abstract operations such as PrepareCalendarFields and CalendarFieldKeysToIgnore to denote a subset of fields to operate on.

Calendar Fields Records have the fields listed in .

Field Name Value Default Property Key Enumeration Key Conversion Meaning
[[Era]] a String or ~unset~ ~unset~ *"era"* ~era~ ~to-string~ A lowercase String value representing the era.
[[EraYear]] an integer or ~unset~ ~unset~ *"eraYear"* ~era-year~ ~to-integer-with-truncation~ The ordinal position of the year within the era.
[[Year]] an integer or ~unset~ ~unset~ *"year"* ~year~ ~to-integer-with-truncation~ The year relative to the first day of a calendar-specific "epoch year".
[[Month]] a positive integer or ~unset~ ~unset~ *"month"* ~month~ ~to-positive-integer-with-truncation~ The 1-based ordinal position of the month within the year.
[[MonthCode]] a String or ~unset~ ~unset~ *"monthCode"* ~month-code~ ~to-month-code~ The month code of the month.
[[Day]] a positive integer or ~unset~ ~unset~ *"day"* ~day~ ~to-positive-integer-with-truncation~ The 1-based ordinal position of the day within the month.
[[Hour]] an integer in the inclusive interval from 0 to 23 or ~unset~ 0 *"hour"* ~hour~ ~to-integer-with-truncation~ The number of the hour within the day.
[[Minute]] an integer in the inclusive interval from 0 to 59 or ~unset~ 0 *"minute"* ~minute~ ~to-integer-with-truncation~ The number of the minute within the hour.
[[Second]] an integer in the inclusive interval from 0 to 59 or ~unset~ 0 *"second"* ~second~ ~to-integer-with-truncation~ The number of the second within the minute.
[[Millisecond]] an integer in the inclusive interval from 0 to 999 or ~unset~ 0 *"millisecond"* ~millisecond~ ~to-integer-with-truncation~ The number of the millisecond within the second.
[[Microsecond]] an integer in the inclusive interval from 0 to 999 or ~unset~ 0 *"microsecond"* ~microsecond~ ~to-integer-with-truncation~ The number of the microsecond within the millisecond.
[[Nanosecond]] an integer in the inclusive interval from 0 to 999 or ~unset~ 0 *"nanosecond"* ~nanosecond~ ~to-integer-with-truncation~ The number of the nanosecond within the microsecond.
[[OffsetString]] a String or ~unset~ ~unset~ *"offset"* ~offset~ ~to-offset-string~ A string of the form `±HH:MM[:SS.SSSSSSSSS]` that can be parsed by ParseDateTimeUTCOffset.
[[TimeZone]] a String or ~unset~ ~unset~ *"timeZone"* ~time-zone~ ~to-temporal-time-zone-identifier~ An available time zone identifier.

PrepareCalendarFields ( _calendar_: a calendar type, _fields_: an Object, _calendarFieldNames_: a List of values from the Enumeration Key column of , _nonCalendarFieldNames_: a List of values from the Enumeration Key column of , _requiredFieldNames_: ~partial~ or a List of values from the Enumeration Key column of , ): either a normal completion containing a Calendar Fields Record, or a throw completion

description
It returns the result of reading from _fields_ all of the property names corresponding to _calendarFieldNames_ and _nonCalendarFieldNames_, plus any extra fields required by the calendar. The returned Record has a non-~unset~ value for each element of _fieldNames_ that corresponds with a non-*undefined* property on _fields_ used as the input for relevant conversion. When _requiredFields_ is ~partial~, this operation throws if none of the properties are present with a non-*undefined* value. When _requiredFields_ is a List, this operation throws if any of the properties named by it are absent or *undefined*.
1. Assert: If _requiredFieldNames_ is a List, _requiredFieldNames_ contains zero or one of each of the elements of _calendarFieldNames_ and _nonCalendarFieldNames_. 1. Let _fieldNames_ be the list-concatenation of _calendarFieldNames_ and _nonCalendarFieldNames_. 1. Let _extraFieldNames_ be CalendarExtraFields(_calendar_, _calendarFieldNames_). 1. Set _fieldNames_ to the list-concatenation of _fieldNames_ and _extraFieldNames_. 1. Assert: _fieldNames_ contains no duplicate elements. 1. Let _result_ be a Calendar Fields Record with all fields equal to ~unset~. 1. Let _any_ be *false*. 1. Let _sortedPropertyNames_ be a List whose elements are the values in the Property Key column of corresponding to the elements of _fieldNames_, sorted according to lexicographic code unit order. 1. For each property name _property_ of _sortedPropertyNames_, do 1. Let _key_ be the value in the Enumeration Key column of corresponding to the row whose Property Key value is _property_. 1. Let _value_ be ? Get(_fields_, _property_). 1. If _value_ is not *undefined*, then 1. Set _any_ to *true*. 1. Let _Conversion_ be the Conversion value of the same row. 1. If _Conversion_ is ~to-integer-with-truncation~, then 1. Set _value_ to ? ToIntegerWithTruncation(_value_). 1. Set _value_ to 𝔽(_value_). 1. Else if _Conversion_ is ~to-positive-integer-with-truncation~, then 1. Set _value_ to ? ToPositiveIntegerWithTruncation(_value_). 1. Set _value_ to 𝔽(_value_). 1. Else if _Conversion_ is ~to-string~, then 1. Set _value_ to ? ToString(_value_). 1. Else if _Conversion_ is ~to-temporal-time-zone-identifier~, then 1. Set _value_ to ? ToTemporalTimeZoneIdentifier(_value_). 1. Else if _Conversion_ is ~to-month-code~, then 1. Set _value_ to ? ToMonthCode(_value_). 1. Else, 1. Assert: _Conversion_ is ~to-offset-string~. 1. Set _value_ to ? ToOffsetString(_value_). 1. Set _result_'s field whose name is given in the Field Name column of the same row to _value_. 1. Else if _requiredFieldNames_ is a List, then 1. If _requiredFieldNames_ contains _key_, then 1. Throw a *TypeError* exception. 1. Set _result_'s field whose name is given in the Field Name column of the same row to the corresponding Default value of the same row. 1. If _requiredFieldNames_ is ~partial~ and _any_ is *false*, then 1. Throw a *TypeError* exception. 1. Return _result_.

CalendarFieldKeysPresent ( _fields_: a Calendar Fields Record, ): a List of values from the Enumeration Key column of

description
The returned List contains the enumeration keys for all fields of _fields_ that are not ~unset~.
1. Let _list_ be « ». 1. For each row of , except the header row, do 1. Let _value_ be _fields_' field whose name is given in the Field Name column of the row. 1. Let _enumerationKey_ be the value in the Enumeration Key column of the row. 1. If _value_ is not ~unset~, append _enumerationKey_ to _list_. 1. Return _list_.

CalendarMergeFields ( _calendar_: a calendar type, _fields_: a Calendar Fields Record, _additionalFields_: a Calendar Fields Record, ): a Calendar Fields Record

description
It merges the properties of _fields_ and _additionalFields_.
1. Let _additionalKeys_ be CalendarFieldKeysPresent(_additionalFields_). 1. Let _overriddenKeys_ be CalendarFieldKeysToIgnore(_calendar_, _additionalKeys_). 1. Let _merged_ be a Calendar Fields Record with all fields set to ~unset~. 1. Let _fieldsKeys_ be CalendarFieldKeysPresent(_fields_). 1. For each row of , except the header row, do 1. Let _key_ be the value in the Enumeration Key column of the row. 1. If _fieldsKeys_ contains _key_ and _overriddenKeys_ does not contain _key_, then 1. Let _propValue_ be _fields_' field whose name is given in the Field Name column of the row. 1. Set _merged_'s field whose name is given in the Field Name column of the row to _propValue_. 1. If _additionalKeys_ contains _key_, then 1. Let _propValue_ be _additionalFields_' field whose name is given in the Field Name column of the row. 1. Set _merged_'s field whose name is given in the Field Name column of the row to _propValue_. 1. Return _merged_.

CalendarDateAdd ( _calendar_: a calendar type, _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
It adds _dateDuration_ to _isoDate_ using the years, months, and weeks reckoning of _calendar_. If addition of years or months results in a nonexistent date, depending on _overflow_ it will be coerced to an existing date or the operation will throw.
1. If _calendar_ is *"iso8601"*, then 1. Let _intermediate_ be BalanceISOYearMonth(_isoDate_.[[Year]] + _duration_.[[Years]], _isoDate_.[[Month]] + _duration_.[[Months]]). 1. Set _intermediate_ to ? RegulateISODate(_intermediate_.[[Year]], _intermediate_.[[Month]], _isoDate_.[[Day]], _overflow_). 1. Let _d_ be _intermediate_.[[Day]] + _duration_.[[Days]] + 7 × _duration_.[[Weeks]]. 1. Let _result_ be BalanceISODate(_intermediate_.[[Year]], _intermediate_.[[Month]], _d_). 1. Else, 1. Let _result_ be an implementation-defined ISO Date Record, or throw a *RangeError* exception, as described below. 1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception. 1. Return _result_.

When _calendar_ is not *"iso8601"*, the operation performs implementation-defined 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_.

CalendarDateUntil ( _calendar_: a calendar type, _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.
1. If _calendar_ is *"iso8601"*, then 1. Let _sign_ be -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 ISODateSurpasses(_sign_, _one_.[[Year]] + _candidateYears_, _one_.[[Month]], _one_.[[Day]], _two_) 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. Let _intermediate_ be BalanceISOYearMonth(_one_.[[Year]] + _years_, _one_.[[Month]] + _candidateMonths_). 1. Repeat, while ISODateSurpasses(_sign_, _intermediate_.[[Year]], _intermediate_.[[Month]], _one_.[[Day]], _two_) is *false*, 1. Set _months_ to _candidateMonths_. 1. Set _candidateMonths_ to _candidateMonths_ + _sign_. 1. Set _intermediate_ to BalanceISOYearMonth(_intermediate_.[[Year]], _intermediate_.[[Month]] + _sign_). 1. Set _intermediate_ to BalanceISOYearMonth(_one_.[[Year]] + _years_, _one_.[[Month]] + _months_). 1. Let _constrained_ be ! RegulateISODate(_intermediate_.[[Year]], _intermediate_.[[Month]], _one_.[[Day]], ~constrain~). 1. Let _weeks_ be 0. 1. If _largestUnit_ is ~week~, then 1. Let _candidateWeeks_ be _sign_. 1. Set _intermediate_ to BalanceISODate(_constrained_.[[Year]], _constrained_.[[Month]], _constrained_.[[Day]] + 7 × _candidateWeeks_). 1. Repeat, while ISODateSurpasses(_sign_, _intermediate_.[[Year]], _intermediate_.[[Month]], _intermediate_.[[Day]], _two_) is *false*, 1. Set _weeks_ to _candidateWeeks_. 1. Set _candidateWeeks_ to _candidateWeeks_ + sign. 1. Set _intermediate_ to BalanceISODate(_intermediate_.[[Year]], _intermediate_.[[Month]], _intermediate_.[[Day]] + 7 × _sign_). 1. Let _days_ be 0. 1. Let _candidateDays_ be _sign_. 1. Set _intermediate_ to BalanceISODate(_constrained_.[[Year]], _constrained_.[[Month]], _constrained_.[[Day]] + 7 × _weeks_ + _candidateDays_). 1. Repeat, while ISODateSurpasses(_sign_, _intermediate_.[[Year]], _intermediate_.[[Month]], _intermediate_.[[Day]], _two_) is *false*, 1. Set _days_ to _candidateDays_. 1. Set _candidateDays_ to _candidateDays_ + _sign_. 1. Set _intermediate_ to BalanceISODate(_intermediate_.[[Year]], _intermediate_.[[Month]], _intermediate_.[[Day]] + _sign_). 1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). 1. Return an implementation-defined Date Duration Record as described above.

ToTemporalCalendarIdentifier ( _temporalCalendarLike_: an ECMAScript value, ): either a normal completion containing a calendar type or a throw completion

description
It attempts to derive a calendar type from _temporalCalendarLike_, and returns that value if found or throws an exception if not.
1. If _temporalCalendarLike_ is an Object, then 1. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then 1. Return _temporalCalendarLike_.[[Calendar]]. 1. If _temporalCalendarLike_ is not a String, throw a *TypeError* exception. 1. Let _identifier_ be ? ParseTemporalCalendarString(_temporalCalendarLike_). 1. Return ? CanonicalizeCalendar(_identifier_).

GetTemporalCalendarIdentifierWithISODefault ( _item_: an Object, ): either a normal completion containing a calendar type or a throw completion

description
It looks for a `calendar` property on the given _item_ and converts its value into a calendar identifier. If no such property is present, the built-in ISO 8601 calendar is returned.
1. If _item_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then 1. Return _item_.[[Calendar]]. 1. Let _calendarLike_ be ? Get(_item_, *"calendar"*). 1. If _calendarLike_ is *undefined*, then 1. Return *"iso8601"*. 1. Return ? ToTemporalCalendarIdentifier(_calendarLike_).

CalendarDateFromFields ( _calendar_: a calendar type, _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 a calendar date in the reckoning of _calendar_, if it is uniquely determined by the fields of _fields_, into an ISO Date Record.
1. Perform ? CalendarResolveFields(_calendar_, _fields_, ~date~). 1. Let _result_ be ? CalendarDateToISO(_calendar_, _fields_, _overflow_). 1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception. 1. Return _result_.

CalendarYearMonthFromFields ( _calendar_: a calendar type, _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 a calendar month in the reckoning of _calendar_, if it is uniquely determined by the properties on _fields_, into an ISO Date Record denoting the first day of that month.
1. Perform ? CalendarResolveFields(_calendar_, _fields_, ~year-month~). 1. Let _firstDayIndex_ be the 1-based index of the first day of the month described by _fields_ (i.e., 1 unless the month's first day is skipped by this calendar.) 1. Set _fields_.[[Day]] to _firstDayIndex_. 1. Let _result_ be ? CalendarDateToISO(_calendar_, _fields_, _overflow_). 1. If ISOYearMonthWithinLimits(_result_) is *false*, throw a *RangeError* exception. 1. Return _result_.

CalendarMonthDayFromFields ( _calendar_: a calendar type, _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 a calendar month-day in the reckoning of _calendar_, if it is uniquely determined by the properties on _fields_, into an ISO Date Record denoting that day in an appropriate reference year.
1. Perform ? CalendarResolveFields(_calendar_, _fields_, ~month-day~). 1. Let _result_ be ? CalendarMonthDayToISOReferenceDate(_calendar_, _fields_, _overflow_). 1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception. 1. Return _result_.

FormatCalendarAnnotation ( _id_: a calendar type, _showCalendar_: ~auto~, ~always~, ~never~, or ~critical~, ): a String

description
It returns a string with a calendar annotation suitable for concatenating to the end of an RFC 9557 string. Depending on the given _id_ and value of _showCalendar_, the string may be empty if no calendar annotation need be included.
1. If _showCalendar_ is ~never~, return the empty String. 1. If _showCalendar_ is ~auto~ and _id_ is *"iso8601"*, return the empty String. 1. If _showCalendar_ is ~critical~, let _flag_ be *"!"*; else, let flag be the empty String. 1. Return the string-concatenation of *"["*, _flag_, *"u-ca="*, _id_, and *"]"*.

CalendarEquals ( _one_: a calendar type, _two_: a calendar type, ): a Boolean

description
It returns *true* if its arguments represent calendars using the same identifier.
1. If CanonicalizeUValue(*"ca"*, _one_) is CanonicalizeUValue(*"ca"*, _two_), return *true*. 1. Return *false*.

ISODaysInMonth ( _year_: an integer, _month_: an integer in the inclusive interval from 1 to 12, ): a positive integer

description
It returns the number of days in the given year and month in the ISO 8601 calendar.
1. If _month_ is 1, 3, 5, 7, 8, 10, or 12, return 31. 1. If _month_ is 4, 6, 9, or 11, return 30. 1. Assert: _month_ = 2. 1. Return 28 + MathematicalInLeapYear(EpochTimeForYear(_year_)).

ISOWeekOfYear ( _isoDate_: an ISO Date Record, ): a Year-Week Record

description
It determines where a calendar day falls in the ISO 8601 week calendar and calculates its calendar week of year, which is the 1-based ordinal number of its calendar week within the corresponding week calendar year (which may differ from _year_ by up to 1 in either direction).
1. Let _year_ be _isoDate_.[[Year]]. 1. Let _wednesday_ be 3. 1. Let _thursday_ be 4. 1. Let _friday_ be 5. 1. Let _saturday_ be 6. 1. Let _daysInWeek_ be 7. 1. Let _maxWeekNumber_ be 53. 1. Let _dayOfYear_ be ISODayOfYear(_isoDate_). 1. Let _dayOfWeek_ be ISODayOfWeek(_isoDate_). 1. Let _week_ be floor((_dayOfYear_ + _daysInWeek_ - _dayOfWeek_ + _wednesday_) / _daysInWeek_). 1. If _week_ < 1, then 1. NOTE: This is the last week of the previous year. 1. Let _jan1st_ be CreateISODateRecord(_year_, 1, 1). 1. Let _dayOfJan1st_ be ISODayOfWeek(_jan1st_). 1. If _dayOfJan1st_ = _friday_, then 1. Return Year-Week Record { [[Week]]: _maxWeekNumber_, [[Year]]: _year_ - 1 }. 1. If _dayOfJan1st_ = _saturday_, and MathematicalInLeapYear(EpochTimeForYear(_year_ - 1)) = 1, then 1. Return Year-Week Record { [[Week]]: _maxWeekNumber_. [[Year]]: _year_ - 1 }. 1. Return Year-Week Record { [[Week]]: _maxWeekNumber_ - 1, [[Year]]: _year_ - 1 }. 1. If _week_ = _maxWeekNumber_, then 1. Let _daysInYear_ be MathematicalDaysInYear(_year_). 1. Let _daysLaterInYear_ be _daysInYear_ - _dayOfYear_. 1. Let _daysAfterThursday_ be _thursday_ - _dayOfWeek_. 1. If _daysLaterInYear_ < _daysAfterThursday_, then 1. Return Year-Week Record { [[Week]]: 1, [[Year]]: _year_ + 1 }. 1. Return Year-Week Record { [[Week]]: _week_, [[Year]]: _year_ }. In the ISO 8601 week calendar, calendar week number 1 of a calendar year is the week including the first Thursday of that year (based on the principle that a week belongs to the same calendar year as the majority of its calendar days), which always includes January 4 and starts on the Monday on or immediately before then. Because of this, some calendar days of the first calendar week of a calendar year may be part of the _preceding_ [proleptic Gregorian] date calendar year, and some calendar days of the last calendar week of a calendar year may be part of the _following_ [proleptic Gregorian] date calendar year. See ISO 8601 for details. For example, week calendar year 2020 includes both 31 December 2019 (a Tuesday belonging to its calendar week 1) and 1 January 2021 (a Friday belonging to its calendar week 53).

ISODayOfYear ( _isoDate_: an ISO Date Record, ): an integer

description
It returns the ISO 8601 calendar day of year of a calendar day, which is its 1-based ordinal number within its ISO 8601 calendar year.
1. Let _epochDays_ be ISODateToEpochDays(_isoDate_.[[Year]], _isoDate_.[[Month]] - 1, _isoDate_.[[Day]]). 1. Return EpochTimeToDayInYear(EpochDaysToEpochMs(_epochDays_, 0)) + 1.

ISODayOfWeek ( _isoDate_: an ISO Date Record, ): an integer

description
It returns the ISO 8601 calendar day of week of a calendar day, which is its 1-based ordinal position within the sequence of week calendar days that starts with Monday at 1 and ends with Sunday at 7.
1. Let _epochDays_ be ISODateToEpochDays(_isoDate_.[[Year]], _isoDate_.[[Month]] - 1, _isoDate_.[[Day]]). 1. Let _dayOfWeek_ be EpochTimeToWeekDay(EpochDaysToEpochMs(_epochDays_, 0)). 1. If _dayOfWeek_ = 0, return 7. 1. Return _dayOfWeek_.

CalendarDateToISO ( _calendar_: a calendar type, _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 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.
1. If _calendar_ is *"iso8601"*, then 1. Assert: _fields_.[[Year]], _fields_.[[Month]], and _fields_.[[Day]] are not ~unset~. 1. Return ? RegulateISODate(_fields_.[[Year]], _fields_.[[Month]], _fields_.[[Day]], _overflow_). 1. Return an implementation-defined ISO Date Record, or throw a *RangeError* exception, as described below.

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:

  • Pick the closest day in the same month. If there are two equally-close dates in that month, pick the later one.
  • If the month is a leap month that doesn't exist in the year, pick another date according to the cultural conventions of that calendar's users. Usually this will result in the same day in the month before or after where that month would normally fall in a leap year.
  • Otherwise, pick the closest date that is still in the same year. If there are two equally-close dates in that year, pick the later one.
  • If the entire year doesn't exist, pick the closest date in a different year. If there are two equally-close dates, pick the later one.

CalendarMonthDayToISOReferenceDate ( _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.
1. If _calendar_ is *"iso8601"*, then 1. Assert: _fields_.[[Month]] and _fields_.[[Day]] are not ~unset~. 1. Let _referenceISOYear_ be 1972 (the first ISO 8601 leap year after the epoch). 1. If _fields_.[[Year]] is ~unset~, let _year_ be _referenceISOYear_; else let _year_ be _fields_.[[Year]]. 1. Let _result_ be ? RegulateISODate(_year_, _fields_.[[Month]], _fields_.[[Day]], _overflow_). 1. Return CreateISODateRecord(_referenceISOYear_, _result_.[[Month]], _result_.[[Day]]). 1. Return an implementation-defined ISO Date Record, or throw a *RangeError* exception, as described below.

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 date is the latest ISO 8601 date corresponding to the calendar date, that is also earlier than or equal to the ISO 8601 date December 31, 1972. If that calendar date never occurs on or before the ISO 8601 date December 31, 1972, then the reference date is the earliest ISO 8601 date corresponding to that calendar date. 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. For example, Hebrew calendar leap month Adar I occurred in calendar years 5730 and 5733 (respectively overlapping ISO 8601 February/March 1970 and February/March 1973), but did not occur between them, so the reference year for days of that month is 1970.

Like RegulateISODate, the operation throws a *RangeError* exception if _overflow_ is ~reject~ and the month and day described by _fields_ does not exist (or does not exist within the year described by _fields_ when there is such a year). For example, when _calendar_ is *"gregory"* and _overflow_ is ~reject~, _fields_ values of { [[MonthCode]]: *"M01"*, [[Day]]: 32 } and { [[Year]]: 2001, [[Month]]: 2, [[Day]]: 29 } would both cause a *RangeError* to be thrown. In the latter case, even though February 29 is a date in leap years of the Gregorian calendar, 2001 was not a leap year and a month code cannot be determined from the nonexistent date 2001-02-29 with the specified month index.

Also like RegulateISODate, if _overflow_ is ~constrain~ and the date or month and day described by _fields_ does not exist (or does not exist within the year described by _fields_ when there is such a year), the values of those fields are clamped to their respective valid range. As in CalendarDateToISO, such clamping is calendar-specific. For example, when _calendar_ is *"gregory"* and _overflow_ is ~constrain~, a _fields_ value of { [[MonthCode]]: *"M02"*, [[Day]]: 30 } is clamped to { [[Year]]: 1972, [[Month]]: 2, [[Day]]: 29 } (because 29 is the maximum valid day for February) while _fields_ values of { [[Year]]: 2001, [[MonthCode]]: *"M02"*, [[Day]]: 30 } and { [[Era]]: *"ce"*, [[EraYear]]: 2001, [[Month]]: 2, [[Day]]: 30 } (or an equivalent with a supported value for [[Era]] representing the Common Era beginning in ISO 8601 year 1) are clamped to { [[Year]]: 1972, [[Month]]: 2, [[Day]]: 28 } (because 28 is the maximum valid day for February 2001, which did not include a leap day).

The operation throws a *RangeError* if _fields_.[[Year]] is not ~unset~ and the ISO 8601 year _year_ corresponding to _fields_.[[Year]] would cause ISODateWithinLimits to return *false* (i.e., _year_ is not in the inclusive interval from -271,821 to 275,760.) This is so as not to require calculating whether the month and day described in _fields_ exist in years arbitrarily far in the future or past. Note this restriction does not apply when _calendar_ is *"iso8601"*.

CalendarISOToDate ( _calendar_: a calendar type, _isoDate_: an ISO Date Record, ): a Calendar Date Record

description
It performs implementation-defined processing to find the the date corresponding to _isoDate_ in the context of the calendar represented by _calendar_ and returns a Calendar Date Record representing that calendar date, with its fields filled in according to their descriptions.
1. If _calendar_ is *"iso8601"*, then 1. Let _monthNumberPart_ be ToZeroPaddedDecimalString(_isoDate_.[[Month]], 2). 1. Let _monthCode_ be the string-concatenation of *"M"* and _monthNumberPart_. 1. If MathematicalInLeapYear(EpochTimeForYear(_isoDate_.[[Year]])) = 1, let _inLeapYear_ be *true*; else let _inLeapYear_ be *false*. 1. Return Calendar Date Record { [[Era]]: *undefined*, [[EraYear]]: *undefined*, [[Year]]: _isoDate_.[[Year]], [[Month]]: _isoDate_.[[Month]], [[MonthCode]]: _monthCode_, [[Day]]: _isoDate_.[[Day]], [[DayOfWeek]]: ISODayOfWeek(_isoDate_), [[DayOfYear]]: ISODayOfYear(_isoDate_), [[WeekOfYear]]: ISOWeekOfYear(_isoDate_), [[DaysInWeek]]: 7, [[DaysInMonth]]: ISODaysInMonth(_isoDate_.[[Year]], _isoDate_.[[Month]]), [[DaysInYear]]: MathematicalDaysInYear(_isoDate_.[[Year]]), [[MonthsInYear]]: 12, [[InLeapYear]]: _inLeapYear_ }. 1. Return an implementation-defined Calendar Date Record with fields as described in .

Implementations are encouraged to elide fields that are not read by the caller.

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_. For example, « ~era~, ~era-year~ » is returned when _calendar_ is *"gregory"* or *"japanese"* and _fields_ is a List containing ~year~.
1. If _calendar_ is *"iso8601"*, return an empty List. 1. Return an implementation-defined List as described above.

CalendarFieldKeysToIgnore ( _calendar_: a calendar type, _keys_: a List of values from the Enumeration Key column of , ): a List of values from the Enumeration Key column of

description
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.
1. If _calendar_ is *"iso8601"*, then 1. Let _ignoredKeys_ be an empty List. 1. For each element _key_ of _keys_, do 1. Append _key_ to _ignoredKeys_. 1. If _key_ is ~month~, append ~month-code~ to _ignoredKeys_. 1. Else if _key_ is ~month-code~, append ~month~ 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_. 1. Return an implementation-defined List as described below.

This operation is relevant for calendars which accept fields other than the standard set of ISO 8601 calendar fields, in order to implement the Temporal objects' `with()` methods in such a way that the result is free of ambiguity or conflicts.

For example, given a _calendar_ that uses eras, such as *"gregory"*, a key in _keys_ being any one of ~year~, ~era~, or ~era-year~ would exclude all three. Passing any one of the three to a `with()` method might conflict with either of the other two properties on the receiver object, so those properties of the receiver object should be ignored. Given this, in addition to the ISO 8601 mutual exclusion of ~month~ and ~month-code~ as above, a possible implementation might produce the following results when _calendar_ is *"gregory"*:

Example results of CalendarFieldKeysToIgnore
_keys_ Returned List
« ~era~ » « ~era~, ~era-year~, ~year~ »
« ~era-year~ » « ~era~, ~era-year~, ~year~ »
« ~year~ » « ~era~, ~era-year~, ~year~ »
« ~month~ » « ~month~, ~month-code~ »
« ~month-code~ » « ~month~, ~month-code~ »
« ~day~ » « ~day~ »
« ~year~, ~month~, ~day~ » « ~era~, ~era-year~, ~year~, ~month~, ~month-code~, ~day~ »
In a _calendar_ such as *"japanese"* where eras do not start and end at year and/or month boundaries, note that the returned List should contain ~era~ and ~era-year~ if _keys_ contains ~day~, ~month~, or ~month-code~ (not only if it contains ~era~, ~era-year~, or ~year~, as in the example above) because it's possible for changing the day or month to cause a conflict with the era.

CalendarResolveFields ( _calendar_: a calendar type, _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 or by merging data that can be represented in multiple forms into standard fields and removing redundant fields (for example, merging [[Month]] and [[MonthCode]] into [[Month]] and merging [[Era]] and [[EraYear]] into [[Year]]).
1. If _calendar_ is *"iso8601"*, then 1. If _type_ is ~date~ or ~year-month~ and _fields_.[[Year]] is ~unset~, throw a *TypeError* exception. 1. If _type_ is ~date~ or ~month-day~ and _fields_.[[Day]] is ~unset~, throw a *TypeError* exception. 1. Let _month_ be _fields_.[[Month]]. 1. Let _monthCode_ be _fields_.[[MonthCode]]. 1. If _monthCode_ is ~unset~, then 1. If _month_ is ~unset~, throw a *TypeError* exception. 1. Return ~unused~. 1. Assert: _monthCode_ is a String. 1. NOTE: The ISO 8601 calendar does not include leap months. 1. If the length of _monthCode_ is not 3, throw a *RangeError* exception. 1. If the first code unit of _monthCode_ is not 0x004D (LATIN CAPITAL LETTER M), throw a *RangeError* exception. 1. Let _monthCodeDigits_ be the substring of _monthCode_ from 1. 1. If ParseText(StringToCodePoints(_monthCodeDigits_), |DateMonth|) is a List of errors, throw a *RangeError* exception. 1. Let _monthCodeInteger_ be ℝ(StringToNumber(_monthCodeDigits_)). 1. If _month_ is not ~unset~ and _month_ ≠ _monthCodeInteger_, throw a *RangeError* exception. 1. Set _fields_.[[Month]] to _monthCodeInteger_. 1. Else, 1. Perform implementation-defined processing to mutate _fields_, or throw a *TypeError* or *RangeError* exception, as described below. 1. Return ~unused~.

The operation throws a *TypeError* exception if the non-~unset~ fields of _fields_ are insufficient to identify a unique instance of _type_ in the calendar (e.g., when at least one field in each combination capable of determining some part of its data is ~unset~) or a *RangeError* exception if the fields are sufficient but their values are internally inconsistent within the calendar (e.g., when fields such as [[Month]] and [[MonthCode]] have conflicting non-~unset~ values). For example:

  • If _type_ is ~date~ or ~month-day~ and "day" in the calendar has an interpretation similar to ISO 8601 and _fields_.[[Day]] is ~unset~.
  • If "month" in the calendar has an interpretation similar to ISO 8601 and either _fields_.[[Month]] and _fields_.[[MonthCode]] are both ~unset~ or neither value is ~unset~ but they do not identify the same month.
  • If _type_ is ~month-day~ and _fields_.[[MonthCode]] is ~unset~ and a specific year cannot be determined from _fields_.
  • If the calendar supports the usual partitioning of years into eras with their own year counting as represented by "year", "era", and "era year" (as in the Gregorian or traditional Japanese calendars) and any of the following cases apply:
    • _type_ is ~date~ or ~year-month~ and each of _fields_.[[Year]], _fields_.[[Era]], and _fields_.[[EraYear]] is ~unset~.
    • _fields_.[[Era]] is ~unset~ but _fields_.[[EraYear]] is not.
    • _fields_.[[EraYear]] is ~unset~ but _fields_.[[Era]] is not.
    • None of the three values are ~unset~ but _fields_.[[Era]] and _fields_.[[EraYear]] do not together identify the same year as _fields_.[[Year]].
In some cases, verifying the internal consistency of two fields requires the data from other fields, such as checking _fields_.[[MonthCode]] *"M06"* against _fields_.[[Month]] 7 in the Hebrew calendar (which are consistent if and only if _fields_ identifies a year that includes leap month Adar I).

When the fields of _fields_ are inconsistent with respect to a non-~unset~ _fields_.[[Era]], it is recommended that _fields_.[[Era]] and _fields_.[[EraYear]] be updated to resolve the inconsistency by lenient interpretation of out-of-bounds values (rather than throwing a *RangeError*), which is particularly useful for consistent interpretation of dates in calendars with regnal eras.

  • In the Gregorian calendar, a zero or negative _fields_.[[EraYear]] should be replaced with a positive [[EraYear]] corresponding with extension of the era into its complement and _fields_.[[Era]] should be updating accordingly (such that Common Era [[EraYear]] 0 is updated to Before Common Era [[EraYear]] 1, Before Common Era [[EraYear]] -1 is updated to Common Era [[EraYear]] 2, etc.).
  • In the Japanese calendar, when _fields_.[[Era]] is not ~unset~ and the date represented by _fields_ is not within the bounds of that era, _fields_.[[Era]] should be updated to the appropriate containing era for that date (for example, because the transition from Heisei era [[EraYear]] 31 to Reiwa era [[EraYear]] 1 took place on May 1 of [[Year]] 2019, Heisei era [[EraYear]] 32 should be updated to Reiwa era [[EraYear]] 2, Reiwa era [[EraYear]] 1 [[Month]] 1 should be updated to Heisei era [[EraYear]] 31 [[Month]] 1, etc.).
When _type_ is ~month-day~ and _fields_.[[Month]] is not ~unset~, it is recommended that all built-in calendars other than the ISO 8601 calendar require a disambiguating year (e.g., either _fields_.[[Year]] or _fields_.[[Era]] and _fields_.[[EraYear]]) to avoid a *TypeError*, regardless of whether or not _fields_.[[MonthCode]] is also ~unset~. The ISO 8601 calendar allows _fields_.[[Year]] to be ~unset~ in this case because it is a special default calendar that is permanently stable for automated processing.