Skip to content

Commit 8138fc3

Browse files
committed
Rewrite Add, Until, and ToISO on top of narrower calendar-dependent AOs
Capitalize "Boolean" RegulateNonISOYearMonth => ConstrainMonthCodeToArithmeticalMonth Change unqualified _month_ to _arithmeticalMonth_ Label a few things as "positive integer" fromIsoDate, toIsoDate
1 parent ca84b8b commit 8138fc3

File tree

2 files changed

+219
-25
lines changed

2 files changed

+219
-25
lines changed

biblio.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,15 @@
133133
"id": "sec-temporal-createmonthcode"
134134
}
135135
]
136+
},
137+
{
138+
"location": "https://tc39.es/ecma262/",
139+
"entries": [
140+
{
141+
"type": "op",
142+
"aoid": "",
143+
"id": ""
144+
}
145+
]
136146
}
137147
]

spec.emu

Lines changed: 209 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ contributors: Google, Ecma International
828828
</h1>
829829
<dl class="header">
830830
<dt>description</dt>
831-
<dd>It produces the year relative to the epoch year (the arithmetic year) for a given set of era, eraYear for a calendar that has eras.</dd>
831+
<dd>It produces the year relative to the epoch year (the arithmetic year) for a given set of _era_, _eraYear_ in _calendar_, a calendar that includes an era named _era_.</dd>
832832
</dl>
833833
<emu-alg>
834834
1. Let _era_ be CanonicalizeEraInCalendar(_calendar_, _era_).
@@ -981,6 +981,169 @@ contributors: Google, Ecma International
981981
</emu-table>
982982
</emu-clause>
983983

984+
<emu-clause id="sec-temporal-calendardatearithmeticaltoiso" type="implementation-defined abstract operation">
985+
<h1>
986+
CalendarDateArithmeticalToISO (
987+
_calendar_: a calendar type that is not *"iso8601"*,
988+
_year_: an integer,
989+
_arithmeticalMonth_: a positive integer,
990+
_day_: an integer,
991+
): either a normal completion containing an ISO Date Record or a throw completion
992+
</h1>
993+
<dl class="header">
994+
<dt>description</dt>
995+
<dd>
996+
It returns an ISO Date Record that, when converted to a Calendar Date Record with _calendar_ (for example, with CalendarISOToDate), contains the given _year_, _arithmeticalMonth_, and _day_ values in its [[Year]], [[Month]], and [[Day]] fields. If this is not possible, a RangeError is thrown.
997+
</dd>
998+
</dl>
999+
<p>It performs the following steps when called:</p>
1000+
<emu-alg>
1001+
1. If _year_, _arithmeticalMonth_, and _day_ do not form a valid date in _calendar_, throw a RangeError exception.
1002+
1. Return a calendar-dependent ISO Date Record as described above.
1003+
</emu-alg>
1004+
</emu-clause>
1005+
1006+
<emu-clause id="sec-temporal-calendardaysinmonth" type="implementation-defined abstract operation">
1007+
<h1>
1008+
CalendarDaysInMonth (
1009+
_calendar_: a calendar type that is not *"iso8601"*,
1010+
_year_: an integer,
1011+
_arithmeticalMonth_: a positive integer,
1012+
): an integer
1013+
</h1>
1014+
<dl class="header">
1015+
<dt>description</dt>
1016+
<dd>
1017+
It interprets _year_ and _arithmeticalMonth_ as arithmetical values in the given _calendar_ and returns the number of days in the corresponding month.
1018+
</dd>
1019+
</dl>
1020+
<p>It performs the following steps when called:</p>
1021+
<emu-alg>
1022+
1. Return a calendar-dependent integer as described above.
1023+
</emu-alg>
1024+
</emu-clause>
1025+
1026+
<emu-clause id="sec-temporal-calendarmonthsinyear" type="implementation-defined abstract operation">
1027+
<h1>
1028+
CalendarMonthsInYear (
1029+
_calendar_: a calendar type that is not *"iso8601"*,
1030+
_year_: an integer,
1031+
): an integer
1032+
</h1>
1033+
<dl class="header">
1034+
<dt>description</dt>
1035+
<dd>
1036+
It interprets _year_ as arithmetical value in the given _calendar_ and returns the number of months in the corresponding year.
1037+
</dd>
1038+
</dl>
1039+
<p>It performs the following steps when called:</p>
1040+
<emu-alg>
1041+
1. Return a calendar-dependent integer as described above.
1042+
</emu-alg>
1043+
</emu-clause>
1044+
1045+
<emu-clause id="sec-temporal-balancenonisodate" type="implementation-defined abstract operation">
1046+
<h1>
1047+
BalanceNonISODate (
1048+
_calendar_: a calendar type that is not *"iso8601"*,
1049+
_year_: an integer,
1050+
_arithmeticalMonth_: an integer,
1051+
_day_: an integer,
1052+
): a Record with fields [[Year]] (an integer), [[Month]] (an integer), and [[Day]] (an integer)
1053+
</h1>
1054+
<dl class="header">
1055+
<dt>description</dt>
1056+
<dd>
1057+
It interprets the given _year_ and potentially out-of-range _arithmeticalMonth_ and _day_ as arithmetical values in the given _calendar_ and returns in-range values by overflowing out-of-range _arithmeticalMonth_ or _day_ values into the next-highest unit.
1058+
This date may be outside the range given by ISODateTimeWithinLimits.
1059+
</dd>
1060+
</dl>
1061+
<p>It performs the following steps when called:</p>
1062+
<emu-alg>
1063+
1. Let _resolvedYear_ be _year_.
1064+
1. Let _resolvedMonth_ be _arithmeticalMonth_.
1065+
1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _resolvedYear_).
1066+
1. Repeat, while _resolvedMonth_ &le; 0,
1067+
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1068+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1069+
1. Set _resolvedMonth_ to _resolvedMonth_ + _monthsInYear_.
1070+
1. Repeat, while _resolvedMonth_ &gt; _monthsInYear_,
1071+
1. Set _resolvedMonth_ to _resolvedMonth_ - _monthsInYear_.
1072+
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1073+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1074+
1. Let _resolvedDay_ be _day_.
1075+
1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1076+
1. Repeat, while _resolvedDay_ &le; 0,
1077+
1. Set _resolvedMonth_ to _resolvedMonth_ - 1.
1078+
1. If _resolvedMonth_ is 0, then
1079+
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1080+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1081+
1. Set _resolvedMonth_ to _monthsInYear_.
1082+
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1083+
1. Set _resolvedDay_ to _day_ + _daysInMonth_.
1084+
1. Repeat, while _resolvedDay_ &gt; _daysInMonth_,
1085+
1. Set _resolvedDay_ to _resolvedDay_ - _daysInMonth_.
1086+
1. Set _resolvedMonth_ to _resolvedMonth_ + 1.
1087+
1. If _resolvedMonth_ &gt; _monthsInYear_, then
1088+
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1089+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1090+
1. Set _resolvedMonth_ to 1.
1091+
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1092+
1. Return the Record { [[Year]]: _resolvedYear_, [[Month]]: _resolvedMonth_, [[Day]]: _resolvedDay_ }.
1093+
</emu-alg>
1094+
</emu-clause>
1095+
1096+
<emu-clause id="sec-temporal-nonisodatesurpasses" type="implementation-defined abstract operation">
1097+
<h1>
1098+
NonISODateSurpasses (
1099+
_calendar_: a calendar type that is not *"iso8601"*,
1100+
_sign_: -1 or 1,
1101+
_fromIsoDate_: an ISO Date Record,
1102+
_toIsoDate_: an ISO Date Record,
1103+
_years_: an integer,
1104+
_months_: an integer,
1105+
_weeks_: an integer,
1106+
_days_: an integer,
1107+
): a Boolean
1108+
</h1>
1109+
<dl class="header">
1110+
<dt>description</dt>
1111+
<dd>
1112+
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_.
1113+
If _weeks_ and _days_ are both zero, then _date1_ need not exist (for example, it could be February 30).
1114+
</dd>
1115+
</dl>
1116+
<p>It performs the following steps when called:</p>
1117+
<emu-alg>
1118+
1. Let _parts_ be CalendarISOToDate(_calendar_, _fromIsoDate_).
1119+
1. Let _y0_ be _parts_.[[Year]] + _years_.
1120+
1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], ~constrain~)).
1121+
1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _months_ + 1, 0).
1122+
1. Let _baseDay_ be _parts_.[[Day]].
1123+
1. If _weeks_ is not 0 or _days_ is not 0, then
1124+
1. If _baseDay_ &lt; _endOfMonth_.[[Day]], then
1125+
1. Let _regulatedDay_ be _baseDay_.
1126+
1. Else,
1127+
1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
1128+
1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _weeks_ + _days_).
1129+
1. Let _y1_ be _balancedDate_.[[Year]].
1130+
1. Let _m1_ be _balancedDate_.[[Month]].
1131+
1. Let _d1_ be _balancedDate_.[[Day]].
1132+
1. Else,
1133+
1. Let _y1_ be _endOfMonth_.[[Year]].
1134+
1. Let _m1_ be _endOfMonth_.[[Month]].
1135+
1. Let _d1_ be _baseDay_.
1136+
1. Let _calDate2_ be CalendarISOToDate(_calendar_, _toIsoDate_).
1137+
1. If _y1_ ≠ _calDate2_.[[Year]], then
1138+
1. If _sign_ × (_y1_ - _calDate2_.[[Year]]) > 0, return *true*.
1139+
1. Else if _m1_ ≠ _calDate2_.[[Month]], then
1140+
1. If _sign_ × (_m1_ - _calDate2_.[[Month]]) > 0, return *true*.
1141+
1. Else if _d1_ ≠ _calDate2_.[[Day]], then
1142+
1. If _sign_ × (_d1_ - _calDate2_.[[Day]]) > 0, return *true*.
1143+
1. Return *false*.
1144+
</emu-alg>
1145+
</emu-clause>
1146+
9841147
<emu-clause id="sup-temporal-nonisodateadd" type="implementation-defined abstract operation">
9851148
<h1>
9861149
NonISODateAdd (
@@ -997,23 +1160,22 @@ contributors: Google, Ecma International
9971160
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_.
9981161
</dd>
9991162
</dl>
1000-
<p>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"*.</p>
1163+
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateAdd for *"iso8601"*.</p>
10011164
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateadd"></emu-xref>.</p>
10021165
<p>It performs the following steps when called:</p>
10031166
<emu-alg>
1004-
1. Let _calendarDate_ be CalendarISOToDate(_calendar_, _isoDate_).
1005-
1. Add _duration_.[[Years]] to _calendarDate_ without changing any less-significant fields.
1006-
1. If YearContainsMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]) is *false*, then
1007-
1. NOTE: This only happens in lunisolar calendars.
1008-
1. Set _calendarDate_.[[MonthCode]] to ! ConstrainMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]], ~constrain~).
1009-
1. Set _calendarDate_.[[Month]] to MonthCodeToOrdinal(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]).
1010-
1. Add _duration_.[[Months]] to _calendarDate_, balancing _calendarDate_ if it goes over a year boundary.
1011-
1. If the date described by _calendarDate_ does not exist, then
1167+
1. Let _parts_ be CalendarISOToDate(_calendar_, _isoDate_).
1168+
1. Let _y0_ be _parts_.[[Year]] + _duration_.[[Years]].
1169+
1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], ~constrain~)).
1170+
1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _duration_.[[Months]] + 1, 0).
1171+
1. Let _baseDay_ be _parts_.[[Day]].
1172+
1. If _endOfMonth_.[[Day]] &lt; _baseDay_, then
10121173
1. If _overflow_ is ~reject~, throw a *RangeError* exception.
1013-
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.
1014-
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.
1015-
1. Add _duration_.[[Weeks]] and _duration_.[[Days]] to _calendarDate_, balancing _calendarDate_ if it goes over a month or year boundary.
1016-
1. Let _result_ be ? CalendarDateToISO(_calendar_, _calendarDate_, _overflow_).
1174+
1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
1175+
1. Else,
1176+
1. Let _regulatedDay_ be _baseDay_.
1177+
1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _duration_.[[Weeks]] + _duration_.[[Days]]).
1178+
1. Let _result_ be ? CalendarDateArithmeticalToISO(_calendar_, _balancedDate_.[[Year]], _balancedDate_.[[Month]], _balancedDate_.[[Day]]).
10171179
1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception.
10181180
1. Return _result_.
10191181
</emu-alg>
@@ -1035,19 +1197,36 @@ contributors: Google, Ecma International
10351197
No fields larger than _largestUnit_ will be non-zero in the resulting Date Duration Record.
10361198
</dd>
10371199
</dl>
1038-
<p>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"*.</p>
1200+
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateUntil for *"iso8601"*.</p>
10391201
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateuntil"></emu-xref>.</p>
10401202
<p>It performs the following steps when called:</p>
10411203
<emu-alg>
1204+
1. Let _sign_ be -CompareISODate(_one_, _two_).
1205+
1. If _sign_ = 0, return ZeroDateDuration().
1206+
1. Let _years_ be 0.
10421207
1. If _largestUnit_ is ~year~, then
1043-
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.
1044-
1. Constrain _one_ to a real year and month, not taking day into account. This step only matters for lunisolar calendars.
1045-
1. If _largestUnit_ is ~year~ or ~month~, then
1046-
1. Add (without constraining) as many months as possible to _one_ without surpassing _two_.
1047-
1. Constrain _one_ to a real year, month, and day.
1048-
1. If _largestUnit_ is ~week~, add as many weeks as possible to _one_ without surpassing _two_.
1049-
1. Add as many days as possible to _one_ until it is equal to _two_.
1050-
1. Return a Date Duration Record of the number of years, months, weeks, and days added.
1208+
1. Let _candidateYears_ be _sign_.
1209+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _candidateYears_, 0, 0, 0) is *false*,
1210+
1. Set _years_ to _candidateYears_.
1211+
1. Set _candidateYears_ to _candidateYears_ + _sign_.
1212+
1. Let _months_ be 0.
1213+
1. If _largestUnit_ is ~year~ or _largestUnit_ is ~month~, then
1214+
1. Let _candidateMonths_ be _sign_.
1215+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _candidateMonths_, 0, 0) is *false*,
1216+
1. Set _months_ to _candidateMonths_.
1217+
1. Set _candidateMonths_ to _candidateMonths_ + _sign_.
1218+
1. Let _weeks_ be 0.
1219+
1. If _largestUnit_ is ~week~, then
1220+
1. Let _candidateWeeks_ be _sign_.
1221+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _candidateWeeks_, 0) is *false*,
1222+
1. Set _weeks_ to _candidateWeeks_.
1223+
1. Set _candidateWeeks_ to _candidateWeeks_ + sign.
1224+
1. Let _days_ be 0.
1225+
1. Let _candidateDays_ be _sign_.
1226+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _weeks_, _candidateDays_) is *false*,
1227+
1. Set _days_ to _candidateDays_.
1228+
1. Set _candidateDays_ to _candidateDays_ + _sign_.
1229+
1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_).
10511230
</emu-alg>
10521231
</emu-clause>
10531232

@@ -1087,8 +1266,13 @@ contributors: Google, Ecma International
10871266
1. Let _ordinalMonth_ be _fields_.[[Month]].
10881267
1. Let _day_ be _fields_.[[Day]].
10891268
1. Assert: _day_ is not ~unset~.
1090-
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.
1091-
1. Return an implementation-defined ISO Date Record that corresponds to the date described by _calendar_, _arithmeticYear_, _ordinalMonth_, and _day_.
1269+
1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _arithmeticYear_, _ordinalMonth_).
1270+
1. If _daysInMonth_ &le; _day_, then
1271+
1. If _overflow_ is ~reject~, throw a *RangeError* exception.
1272+
1. Let _regulatedDay_ be _daysInMonth_.
1273+
1. Else,
1274+
1. Let _regulatedDay_ be _day_.
1275+
1. Return ? CalendarDateArithmeticalToISO(_calendar_, _arithmeticYear_, _ordinalMonth_, _regulatedDay_).
10921276
</emu-alg>
10931277
</emu-clause>
10941278

0 commit comments

Comments
 (0)