diff --git a/biblio.json b/biblio.json
index ec97db0..b213f38 100644
--- a/biblio.json
+++ b/biblio.json
@@ -121,6 +121,16 @@
"type": "op",
"aoid": "ZeroDateDuration",
"id": "sec-temporal-zerodateduration"
+ },
+ {
+ "type": "op",
+ "aoid": "ParseMonthCode",
+ "id": "sec-temporal-parsemonthcode"
+ },
+ {
+ "type": "op",
+ "aoid": "CreateMonthCode",
+ "id": "sec-temporal-createmonthcode"
}
]
}
diff --git a/spec.emu b/spec.emu
index ead01a2..6cd16c6 100644
--- a/spec.emu
+++ b/spec.emu
@@ -533,36 +533,135 @@ contributors: Google, Ecma International
Calendar |
Additional Month Codes |
+ Leap 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
+
+
+ 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
+
+
+ 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 which the _calendar_ is in the Calendar column.
+ 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
+
+
+ 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: The following loop will terminate.
+ 1. Repeat, while _number_ ≤ 12,
+ 1. Let _currentMonthCode_ be CreateMonthCode(_number_, _isLeap_).
+ 1. If YearContainsMonthCode(_calendar_, _arithmeticYear_, _currentMonthCode_), 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.
+
+
+
IsValidEraYearForCalendar (
@@ -903,10 +1002,11 @@ contributors: Google, Ecma International
It performs the following steps when called:
1. Let _calendarDate_ be CalendarISOToDate(_calendar_, _isoDate_).
- 1. Add _duration_.[[Years]] to _calendarDate_.
- 1. (This step only matters for lunisolar calendars.) If _calendarDate_.[[MonthCode]] is a leap month that doesn't exist in the year, then:
- 1. Set _calendarDate_ to another date according to the cultural conventions of that calendar's users. Of the currently supported calendars: if _calendar_ is *"chinese"* or *"dangi"*, change _calendarDate_.[[MonthCode]] to the same month code but without the *"L"*. If _calendar_ is *"hebrew"*, change _calendarDate_.[[MonthCode]] from *"M05L"* to *"M06"*.
- 1. Update _calendarDate_.[[Month]] accordingly.
+ 1. Add _duration_.[[Years]] to _calendarDate_ without changing any less-significant fields.
+ 1. If YearContainsMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]) is *false*, then
+ 1. NOTE: This only happens in lunisolar calendars.
+ 1. Set _calendarDate_.[[MonthCode]] to ! ConstrainMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]], ~constrain~).
+ 1. Set _calendarDate_.[[Month]] to MonthCodeToOrdinal(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]).
1. Add _duration_.[[Months]] to _calendarDate_, balancing _calendarDate_ if it goes over a year boundary.
1. If the date described by _calendarDate_ does not exist, then
1. If _overflow_ is ~reject~, throw a *RangeError* exception.
@@ -972,16 +1072,23 @@ contributors: Google, Ecma International
This definition supersedes the definition provided in .
It performs the following steps when called:
- 1. If _fields_.[[Era]] and _fields_.[[EraYear]] are not ~unset~ and IsValidEraYearForCalendar(_calendar_, _fields_.[[Era]], _fields_.[[EraYear]]) is *false*, throw a *RangeError* exception.
- 1. If _fields_.[[MonthCode]] is not ~unset~ and IsValidMonthCodeForCalendar(_calendar_, _fields_.[[MonthCode]]) is *false*, throw a *RangeError* exception.
- 1. If _fields_ describes an existing date in _calendar_, return an implementation-defined ISO Date Record that corresponds to the date described by _fields_.
- 1. If _overflow_ is ~reject~, throw a *RangeError* exception.
- 1. (This step only matters for lunisolar calendars.) If _fields_.[[MonthCode]] is a leap month that doesn't exist in the year, then:
- 1. Set _fields_ to another date according to the cultural conventions of that calendar's users. Of the currently supported calendars: if _calendar_ is *"chinese"* or *"dangi"*, change _fields_.[[MonthCode]] to the same month code but without the *"L"*. If _calendar_ is *"hebrew"*, change _fields_.[[MonthCode]] from *"M05L"* to *"M06"*.
- 1. Update _fields_.[[Month]] accordingly.
- 1. If _fields_.[[MonthCode]] is a valid month code for _fields_.[[Year]], but the date described by _fields_ does not exist, set _fields_.[[Day]] to the closest day in the same month. If there are two equally-close dates in the same month, pick the later one.
- 1. (This step does not apply to any currently supported calendars.) If the date described by _fields_ still does not exist, set _fields_ to the closest date in the same year. If there are two equally-close dates in that year, pick the later one.
- 1. Return an implementation-defined ISO Date Record that corresponds to the date described by _fields_.
+ 1. If _fields_.[[Era]] and _fields_.[[EraYear]] are not ~unset~, then
+ 1. If IsValidEraYearForCalendar(_calendar_, _fields_.[[Era]], _fields_.[[EraYear]]) is *false*, throw a *RangeError* exception.
+ 1. Let _arithmeticYear_ be CalendarDateArithmeticYearForEraYear(_calendar_, _fields_.[[Era]], _fields_.[[EraYear]]).
+ 1. Else,
+ 1. Assert: _fields_.[[Year]] is not ~unset~.
+ 1. Let _arithmeticYear_ be _fields_.[[Year]].
+ 1. If _fields_.[[MonthCode]] is not ~unset~, then
+ 1. If IsValidMonthCodeForCalendar(_calendar_, _fields_.[[MonthCode]]) is *false*, throw a *RangeError* exception.
+ 1. Let _constrainedMonthCode_ be ? ConstrainMonthCode(_calendar_, _arithmeticYear_, _fields_.[[MonthCode]], _overflow_).
+ 1. Let _ordinalMonth_ be MonthCodeToOrdinal(_calendar_, _arithmeticYear_, _constrainedMonthCode_).
+ 1. Else,
+ 1. Assert: _fields_.[[Month]] is not ~unset~.
+ 1. Let _ordinalMonth_ be _fields_.[[Month]].
+ 1. Let _day_ be _fields_.[[Day]].
+ 1. Assert: _day_ is not ~unset~.
+ 1. If the date described by _calendar_, _arithmeticYear_, _ordinalMonth_, and _day_ does not exist, set _day_ to the closest day in the same month. If there are two equally-close dates in the same month, pick the later one.
+ 1. Return an implementation-defined ISO Date Record that corresponds to the date described by _calendar_, _arithmeticYear_, _ordinalMonth_, and _day_.