NonISODateAdd (
@@ -997,23 +1162,26 @@ contributors: Google, Ecma International
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_.
-
The behaviour is implementation-defined, but all calendars follow the general steps given here, which is a generalization of the precise algorithm specified in CalendarDateAdd for *"iso8601"*.
+ 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 _calendarDate_ be CalendarISOToDate(_calendar_, _isoDate_).
- 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. 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]], ~constrain~)).
+ 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. If _overflow_ is ~reject~, throw a *RangeError* exception.
- 1. If _calendarDate_.[[MonthCode]] is a valid month code for _calendarDate_.[[Year]], but the date described by _calendarDate_ does not exist, set _calendarDate_.[[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 _calendarDate_ still does not exist, set _calendarDate_ to the closest date in the same year. If there are two equally-close dates in that year, pick the later one.
- 1. Add _duration_.[[Weeks]] and _duration_.[[Days]] to _calendarDate_, balancing _calendarDate_ if it goes over a month or year boundary.
- 1. Let _result_ be ? CalendarDateToISO(_calendar_, _calendarDate_, _overflow_).
+ 1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
+ 1. Else,
+ 1. Let _regulatedDay_ be _baseDay_.
+ 1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _duration_.[[Weeks]] + _duration_.[[Days]]).
+ 1. Let _result_ be ? CalendarDateArithmeticalToISO(_calendar_, _balancedDate_.[[Year]], _balancedDate_.[[Month]], _balancedDate_.[[Day]]).
1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception.
1. Return _result_.
@@ -1035,19 +1203,36 @@ contributors: Google, Ecma International
No fields larger than _largestUnit_ will be non-zero in the resulting Date Duration Record.
- The algorithm is implementation-defined, but all calendars follow the general steps given here, which is a generalization of the precise algorithm specified in CalendarDateUntil for *"iso8601"*.
+ 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. Add (without constraining) as many years as possible to _one_, in the direction from _one_ to _two_, without surpassing _two_. "Surpassing" here (and in all steps below) means to compare years numerically, then month codes lexicographically, then days numerically; if any of them exceed _two_ in the direction from _one_ to _two_, then _two_ is surpassed.
- 1. Constrain _one_ to a real year and month, not taking day into account. This step only matters for lunisolar calendars.
- 1. If _largestUnit_ is ~year~ or ~month~, then
- 1. Add (without constraining) as many months as possible to _one_ without surpassing _two_.
- 1. Constrain _one_ to a real year, month, and day.
- 1. If _largestUnit_ is ~week~, add as many weeks as possible to _one_ without surpassing _two_.
- 1. Add as many days as possible to _one_ until it is equal to _two_.
- 1. Return a Date Duration Record of the number of years, months, weeks, and days added.
+ 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_).
@@ -1087,8 +1272,13 @@ contributors: Google, Ecma International
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_.
+ 1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _arithmeticYear_, _ordinalMonth_).
+ 1. If _daysInMonth_ ≤ _day_, then
+ 1. If _overflow_ is ~reject~, throw a *RangeError* exception.
+ 1. Let _regulatedDay_ be _daysInMonth_.
+ 1. Else,
+ 1. Let _regulatedDay_ be _day_.
+ 1. Return ? CalendarDateArithmeticalToISO(_calendar_, _arithmeticYear_, _ordinalMonth_, _regulatedDay_).