Skip to content

Commit 0a7577f

Browse files
sffcgibson042ptomato
authored
Use more specific calendar-dependent operations in Until, Add, and ToISO (#69)
Co-authored-by: Richard Gibson <[email protected]> Co-authored-by: Philip Chimento <[email protected]>
1 parent 0fb3db2 commit 0a7577f

File tree

2 files changed

+171
-23
lines changed

2 files changed

+171
-23
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: 161 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ contributors: Google, Ecma International
864864
</h1>
865865
<dl class="header">
866866
<dt>description</dt>
867-
<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>
867+
<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>
868868
</dl>
869869
<emu-alg>
870870
1. Let _era_ be CanonicalizeEraInCalendar(_calendar_, _era_).
@@ -1040,6 +1040,128 @@ contributors: Google, Ecma International
10401040
</emu-table>
10411041
</emu-clause>
10421042

1043+
<emu-clause id="sec-temporal-calendarmonthsinyear" type="implementation-defined abstract operation">
1044+
<h1>
1045+
CalendarMonthsInYear (
1046+
_calendar_: a calendar type that is not *"iso8601"*,
1047+
_arithmeticYear_: an integer,
1048+
): a positive integer
1049+
</h1>
1050+
<dl class="header">
1051+
<dt>description</dt>
1052+
<dd>
1053+
It interprets _arithmeticYear_ as a year in the given calendar in the same way as CalendarDateArithmeticYear, and returns the number of months in that year.
1054+
</dd>
1055+
</dl>
1056+
<p>It performs the following steps when called:</p>
1057+
<emu-alg>
1058+
1. Return the number of months in arithmetic year _arithmeticYear_ of calendar _calendar_.
1059+
</emu-alg>
1060+
</emu-clause>
1061+
1062+
<emu-clause id="sec-temporal-balancenonisodate" type="implementation-defined abstract operation">
1063+
<h1>
1064+
BalanceNonISODate (
1065+
_calendar_: a calendar type that is not *"iso8601"*,
1066+
_arithmeticYear_: an integer,
1067+
_ordinalMonth_: a positive integer,
1068+
_day_: a positive integer,
1069+
): a Record with fields [[Year]] (an integer), [[Month]] (a positive integer), and [[Day]] (a positive integer)
1070+
</h1>
1071+
<dl class="header">
1072+
<dt>description</dt>
1073+
<dd>
1074+
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.
1075+
This date may correspond to a date in the ISO calendar that is outside the range given by ISODateTimeWithinLimits.
1076+
</dd>
1077+
</dl>
1078+
<p>It performs the following steps when called:</p>
1079+
<emu-alg>
1080+
1. Let _resolvedYear_ be _arithmeticYear_.
1081+
1. Let _resolvedMonth_ be _ordinalMonth_.
1082+
1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _resolvedYear_).
1083+
1. Repeat, while _resolvedMonth_ &le; 0,
1084+
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1085+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1086+
1. Set _resolvedMonth_ to _resolvedMonth_ + _monthsInYear_.
1087+
1. Repeat, while _resolvedMonth_ &gt; _monthsInYear_,
1088+
1. Set _resolvedMonth_ to _resolvedMonth_ - _monthsInYear_.
1089+
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1090+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1091+
1. Let _resolvedDay_ be _day_.
1092+
1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1093+
1. Repeat, while _resolvedDay_ &le; 0,
1094+
1. Set _resolvedMonth_ to _resolvedMonth_ - 1.
1095+
1. If _resolvedMonth_ is 0, then
1096+
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1097+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1098+
1. Set _resolvedMonth_ to _monthsInYear_.
1099+
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1100+
1. Set _resolvedDay_ to _resolvedDay_ + _daysInMonth_.
1101+
1. Repeat, while _resolvedDay_ &gt; _daysInMonth_,
1102+
1. Set _resolvedDay_ to _resolvedDay_ - _daysInMonth_.
1103+
1. Set _resolvedMonth_ to _resolvedMonth_ + 1.
1104+
1. If _resolvedMonth_ &gt; _monthsInYear_, then
1105+
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1106+
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1107+
1. Set _resolvedMonth_ to 1.
1108+
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1109+
1. Return the Record { [[Year]]: _resolvedYear_, [[Month]]: _resolvedMonth_, [[Day]]: _resolvedDay_ }.
1110+
</emu-alg>
1111+
</emu-clause>
1112+
1113+
<emu-clause id="sec-temporal-nonisodatesurpasses" type="implementation-defined abstract operation">
1114+
<h1>
1115+
NonISODateSurpasses (
1116+
_calendar_: a calendar type that is not *"iso8601"*,
1117+
_sign_: -1 or 1,
1118+
_fromIsoDate_: an ISO Date Record,
1119+
_toIsoDate_: an ISO Date Record,
1120+
_years_: an integer,
1121+
_months_: an integer,
1122+
_weeks_: an integer,
1123+
_days_: an integer,
1124+
): a Boolean
1125+
</h1>
1126+
<dl class="header">
1127+
<dt>description</dt>
1128+
<dd>
1129+
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_.
1130+
If _weeks_ and _days_ are both zero, then _date1_ need not exist (for example, it could be February 30).
1131+
</dd>
1132+
</dl>
1133+
<p>It performs the following steps when called:</p>
1134+
<emu-alg>
1135+
1. Let _parts_ be CalendarISOToDate(_calendar_, _fromIsoDate_).
1136+
1. Let _y0_ be _parts_.[[Year]] + _years_.
1137+
1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], ~constrain~)).
1138+
1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _months_ + 1, 0).
1139+
1. Let _baseDay_ be _parts_.[[Day]].
1140+
1. If _weeks_ is not 0 or _days_ is not 0, then
1141+
1. If _baseDay_ &lt; _endOfMonth_.[[Day]], then
1142+
1. Let _regulatedDay_ be _baseDay_.
1143+
1. Else,
1144+
1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
1145+
1. Let _daysInWeek_ be 7 (the number of days in a week for all supported calendars).
1146+
1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + _daysInWeek_ * _weeks_ + _days_).
1147+
1. Let _y1_ be _balancedDate_.[[Year]].
1148+
1. Let _m1_ be _balancedDate_.[[Month]].
1149+
1. Let _d1_ be _balancedDate_.[[Day]].
1150+
1. Else,
1151+
1. Let _y1_ be _endOfMonth_.[[Year]].
1152+
1. Let _m1_ be _endOfMonth_.[[Month]].
1153+
1. Let _d1_ be _baseDay_.
1154+
1. Let _calDate2_ be CalendarISOToDate(_calendar_, _toIsoDate_).
1155+
1. If _y1_ ≠ _calDate2_.[[Year]], then
1156+
1. If _sign_ × (_y1_ - _calDate2_.[[Year]]) > 0, return *true*.
1157+
1. Else if _m1_ ≠ _calDate2_.[[Month]], then
1158+
1. If _sign_ × (_m1_ - _calDate2_.[[Month]]) > 0, return *true*.
1159+
1. Else if _d1_ ≠ _calDate2_.[[Day]], then
1160+
1. If _sign_ × (_d1_ - _calDate2_.[[Day]]) > 0, return *true*.
1161+
1. Return *false*.
1162+
</emu-alg>
1163+
</emu-clause>
1164+
10431165
<emu-clause id="sup-temporal-nonisodateadd" type="implementation-defined abstract operation">
10441166
<h1>
10451167
NonISODateAdd (
@@ -1056,23 +1178,22 @@ contributors: Google, Ecma International
10561178
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_.
10571179
</dd>
10581180
</dl>
1059-
<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>
1181+
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateAdd for *"iso8601"*.</p>
10601182
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateadd"></emu-xref>.</p>
10611183
<p>It performs the following steps when called:</p>
10621184
<emu-alg>
1063-
1. Let _calendarDate_ be CalendarISOToDate(_calendar_, _isoDate_).
1064-
1. Add _duration_.[[Years]] to _calendarDate_ without changing any less-significant fields.
1065-
1. If YearContainsMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]) is *false*, then
1066-
1. NOTE: This only happens in lunisolar calendars.
1067-
1. Set _calendarDate_.[[MonthCode]] to ! ConstrainMonthCode(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]], _overflow_).
1068-
1. Set _calendarDate_.[[Month]] to MonthCodeToOrdinal(_calendar_, _calendarDate_.[[Year]], _calendarDate_.[[MonthCode]]).
1069-
1. Add _duration_.[[Months]] to _calendarDate_, balancing _calendarDate_ if it goes over a year boundary.
1070-
1. If the date described by _calendarDate_ does not exist, then
1185+
1. Let _parts_ be CalendarISOToDate(_calendar_, _isoDate_).
1186+
1. Let _y0_ be _parts_.[[Year]] + _duration_.[[Years]].
1187+
1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], _overflow_)).
1188+
1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _duration_.[[Months]] + 1, 0).
1189+
1. Let _baseDay_ be _parts_.[[Day]].
1190+
1. If _baseDay_ &lt; _endOfMonth_.[[Day]], then
1191+
1. Let _regulatedDay_ be _baseDay_.
1192+
1. Else,
10711193
1. If _overflow_ is ~reject~, throw a *RangeError* exception.
1072-
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.
1073-
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.
1074-
1. Add _duration_.[[Weeks]] and _duration_.[[Days]] to _calendarDate_, balancing _calendarDate_ if it goes over a month or year boundary.
1075-
1. Let _result_ be ? CalendarDateToISO(_calendar_, _calendarDate_, _overflow_).
1194+
1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
1195+
1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _duration_.[[Weeks]] + _duration_.[[Days]]).
1196+
1. Let _result_ be ? CalendarIntegersToISO(_calendar_, _balancedDate_.[[Year]], _balancedDate_.[[Month]], _balancedDate_.[[Day]]).
10761197
1. If ISODateWithinLimits(_result_) is *false*, throw a *RangeError* exception.
10771198
1. Return _result_.
10781199
</emu-alg>
@@ -1094,19 +1215,36 @@ contributors: Google, Ecma International
10941215
No fields larger than _largestUnit_ will be non-zero in the resulting Date Duration Record.
10951216
</dd>
10961217
</dl>
1097-
<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>
1218+
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateUntil for *"iso8601"*.</p>
10981219
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateuntil"></emu-xref>.</p>
10991220
<p>It performs the following steps when called:</p>
11001221
<emu-alg>
1222+
1. Let _sign_ be -1 × CompareISODate(_one_, _two_).
1223+
1. If _sign_ = 0, return ZeroDateDuration().
1224+
1. Let _years_ be 0.
11011225
1. If _largestUnit_ is ~year~, then
1102-
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.
1103-
1. Constrain _one_ to a real year and month, not taking day into account. This step only matters for lunisolar calendars.
1104-
1. If _largestUnit_ is ~year~ or ~month~, then
1105-
1. Add (without constraining) as many months as possible to _one_ without surpassing _two_.
1106-
1. Constrain _one_ to a real year, month, and day.
1107-
1. If _largestUnit_ is ~week~, add as many weeks as possible to _one_ without surpassing _two_.
1108-
1. Add as many days as possible to _one_ until it is equal to _two_.
1109-
1. Return a Date Duration Record of the number of years, months, weeks, and days added.
1226+
1. Let _candidateYears_ be _sign_.
1227+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _candidateYears_, 0, 0, 0) is *false*,
1228+
1. Set _years_ to _candidateYears_.
1229+
1. Set _candidateYears_ to _candidateYears_ + _sign_.
1230+
1. Let _months_ be 0.
1231+
1. If _largestUnit_ is ~year~ or _largestUnit_ is ~month~, then
1232+
1. Let _candidateMonths_ be _sign_.
1233+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _candidateMonths_, 0, 0) is *false*,
1234+
1. Set _months_ to _candidateMonths_.
1235+
1. Set _candidateMonths_ to _candidateMonths_ + _sign_.
1236+
1. Let _weeks_ be 0.
1237+
1. If _largestUnit_ is ~week~, then
1238+
1. Let _candidateWeeks_ be _sign_.
1239+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _candidateWeeks_, 0) is *false*,
1240+
1. Set _weeks_ to _candidateWeeks_.
1241+
1. Set _candidateWeeks_ to _candidateWeeks_ + sign.
1242+
1. Let _days_ be 0.
1243+
1. Let _candidateDays_ be _sign_.
1244+
1. Repeat, while NonISODateSurpasses(_calendar_, _sign_, _one_, _two_, _years_, _months_, _weeks_, _candidateDays_) is *false*,
1245+
1. Set _days_ to _candidateDays_.
1246+
1. Set _candidateDays_ to _candidateDays_ + _sign_.
1247+
1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_).
11101248
</emu-alg>
11111249
</emu-clause>
11121250

0 commit comments

Comments
 (0)