Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions biblio.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,15 @@
"id": "sec-temporal-createmonthcode"
}
]
},
{
"location": "https://tc39.es/ecma262/",
"entries": [
{
"type": "op",
"aoid": "ℝ",
"id": "ℝ"
}
]
Comment on lines +136 to +145
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing to do with the substance of the PR, but I'm surprised this doesn't get linked automatically with --load-biblio @tc39/ecma262-biblio!

}
]
234 changes: 209 additions & 25 deletions spec.emu
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ contributors: Google, Ecma International
</h1>
<dl class="header">
<dt>description</dt>
<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>
<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>
</dl>
<emu-alg>
1. Let _era_ be CanonicalizeEraInCalendar(_calendar_, _era_).
Expand Down Expand Up @@ -981,6 +981,169 @@ contributors: Google, Ecma International
</emu-table>
</emu-clause>

<emu-clause id="sec-temporal-calendardatearithmeticaltoiso" type="implementation-defined abstract operation">
<h1>
CalendarDateArithmeticalToISO (
_calendar_: a calendar type that is not *"iso8601"*,
_arithmeticYear_: an integer,
_ordinalMonth_: a positive integer,
_day_: an integer,
): either a normal completion containing an ISO Date Record or a throw completion
</h1>
<dl class="header">
<dt>description</dt>
<dd>
It returns an ISO Date Record that, when converted to a Calendar Date Record with _calendar_ (for example, with CalendarISOToDate), contains the given _arithmeticYear_, _ordinalMonth_, and _day_ values in its [[Year]], [[Month]], and [[Day]] fields. If this is not possible, a RangeError is thrown.
</dd>
</dl>
<p>It performs the following steps when called:</p>
<emu-alg>
1. If _arithmeticYear_, _ordinalMonth_, and _day_ do not form a valid date in _calendar_, throw a RangeError exception.
1. Return a calendar-dependent ISO Date Record as described above.
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-calendardaysinmonth" type="implementation-defined abstract operation">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor point, I would suggest deleting the one-line calendar-dependent operations CalendarDaysInMonth and CalendarMonthsInYear. We used to have those in Temporal and got rid of them because they take up space without really adding anything meaningful. Instead, I'd just put something in NonISODateAdd etc. like "Let monthsInYear be the calendar-dependent number of months in year."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gibson042 suggested smaller AOs earlier in #69 (comment). I don't have a strong opinion except that it's kind-of nice to have a "googlable" place to plug things in.

<h1>
CalendarDaysInMonth (
_calendar_: a calendar type that is not *"iso8601"*,
_arithmeticYear_: an integer,
_ordinalMonth_: a positive integer,
): an integer
</h1>
<dl class="header">
<dt>description</dt>
<dd>
It interprets _arithmeticYear_ and _ordinalMonth_ as arithmetical values in the given _calendar_ and returns the number of days in the corresponding month.
</dd>
</dl>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Return a calendar-dependent integer as described above.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think operation description text is considered normative, so this use of "as described above" in the algorithms for CalendarDateArithmeticalToISO, CalendarDaysInMonth, and CalendarMonthsInYear (which are normative) is invalid. Please refactor accordingly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got the "as described above" language from the 262 version of CalendarExtraFields:

The implementation-defined abstract operation CalendarExtraFields takes arguments calendar (a calendar type) and fields (a List of values from the Enumeration Key column of Table 19) and returns a List of values from the Enumeration Key column of Table 19. 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. It performs the following steps when called:

  1. If calendar is "iso8601", return an empty List.
  2. Return an implementation-defined List as described above.

https://tc39.es/proposal-temporal/#sec-temporal-calendarextrafields

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect the ECMA-262 editors to have analogous comments on that text.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you suggest a specific change here? Should I:

  1. Repeat the prose in the <emu-alg> and keep it in the <dl class="header">
  2. Move the prose from the <dl class="header"> into the <emu-alg> and write a less specific description for the <dl class="header">
  3. Attempt to write out the prose in steps in the <emu-alg>
  4. Something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Number 2 or 3 both seem like good approaches.

</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-calendarmonthsinyear" type="implementation-defined abstract operation">
<h1>
CalendarMonthsInYear (
_calendar_: a calendar type that is not *"iso8601"*,
_arithmeticYear_: an integer,
): an integer
</h1>
<dl class="header">
<dt>description</dt>
<dd>
It interprets _arithmeticYear_ as arithmetical value in the given _calendar_ and returns the number of months in the corresponding year.
</dd>
</dl>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Return a calendar-dependent integer as described above.
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-balancenonisodate" type="implementation-defined abstract operation">
<h1>
BalanceNonISODate (
_calendar_: a calendar type that is not *"iso8601"*,
_arithmeticYear_: an integer,
_ordinalMonth_: an integer,
_day_: an integer,
): a Record with fields [[Year]] (an integer), [[Month]] (an integer), and [[Day]] (an integer)
</h1>
<dl class="header">
<dt>description</dt>
<dd>
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.
This date may be outside the range given by ISODateTimeWithinLimits.
</dd>
</dl>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Let _resolvedYear_ be _arithmeticYear_.
1. Let _resolvedMonth_ be _ordinalMonth_.
1. Let _monthsInYear_ be CalendarMonthsInYear(_calendar_, _resolvedYear_).
1. Repeat, while _resolvedMonth_ &le; 0,
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1. Set _resolvedMonth_ to _resolvedMonth_ + _monthsInYear_.
1. Repeat, while _resolvedMonth_ &gt; _monthsInYear_,
1. Set _resolvedMonth_ to _resolvedMonth_ - _monthsInYear_.
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1. Let _resolvedDay_ be _day_.
1. Let _daysInMonth_ be CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1. Repeat, while _resolvedDay_ &le; 0,
1. Set _resolvedMonth_ to _resolvedMonth_ - 1.
1. If _resolvedMonth_ is 0, then
1. Set _resolvedYear_ to _resolvedYear_ - 1.
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1. Set _resolvedMonth_ to _monthsInYear_.
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1. Set _resolvedDay_ to _day_ + _daysInMonth_.
1. Repeat, while _resolvedDay_ &gt; _daysInMonth_,
1. Set _resolvedDay_ to _resolvedDay_ - _daysInMonth_.
1. Set _resolvedMonth_ to _resolvedMonth_ + 1.
1. If _resolvedMonth_ &gt; _monthsInYear_, then
1. Set _resolvedYear_ to _resolvedYear_ + 1.
1. Set _monthsInYear_ to CalendarMonthsInYear(_calendar_, _resolvedYear_).
1. Set _resolvedMonth_ to 1.
1. Set _daysInMonth_ to CalendarDaysInMonth(_calendar_, _resolvedYear_, _resolvedMonth_).
1. Return the Record { [[Year]]: _resolvedYear_, [[Month]]: _resolvedMonth_, [[Day]]: _resolvedDay_ }.
</emu-alg>
</emu-clause>

<emu-clause id="sec-temporal-nonisodatesurpasses" type="implementation-defined abstract operation">
<h1>
NonISODateSurpasses (
_calendar_: a calendar type that is not *"iso8601"*,
_sign_: -1 or 1,
_fromIsoDate_: an ISO Date Record,
_toIsoDate_: an ISO Date Record,
_years_: an integer,
_months_: an integer,
_weeks_: an integer,
_days_: an integer,
): a Boolean
</h1>
<dl class="header">
<dt>description</dt>
<dd>
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_.
If _weeks_ and _days_ are both zero, then _date1_ need not exist (for example, it could be February 30).
</dd>
</dl>
<p>It performs the following steps when called:</p>
<emu-alg>
1. Let _parts_ be CalendarISOToDate(_calendar_, _fromIsoDate_).
1. Let _y0_ be _parts_.[[Year]] + _years_.
1. Let _m0_ be MonthCodeToOrdinal(_calendar_, _y0_, ! ConstrainMonthCode(_calendar_, _y0_, _parts_.[[MonthCode]], ~constrain~)).
1. Let _endOfMonth_ be BalanceNonISODate(_calendar_, _y0_, _m0_ + _months_ + 1, 0).
1. Let _baseDay_ be _parts_.[[Day]].
1. If _weeks_ is not 0 or _days_ is not 0, then
1. If _baseDay_ &lt; _endOfMonth_.[[Day]], then
1. Let _regulatedDay_ be _baseDay_.
1. Else,
1. Let _regulatedDay_ be _endOfMonth_.[[Day]].
1. Let _balancedDate_ be BalanceNonISODate(_calendar_, _endOfMonth_.[[Year]], _endOfMonth_.[[Month]], _regulatedDay_ + 7 * _weeks_ + _days_).
1. Let _y1_ be _balancedDate_.[[Year]].
1. Let _m1_ be _balancedDate_.[[Month]].
1. Let _d1_ be _balancedDate_.[[Day]].
1. Else,
1. Let _y1_ be _endOfMonth_.[[Year]].
1. Let _m1_ be _endOfMonth_.[[Month]].
1. Let _d1_ be _baseDay_.
1. Let _calDate2_ be CalendarISOToDate(_calendar_, _toIsoDate_).
1. If _y1_ ≠ _calDate2_.[[Year]], then
1. If _sign_ × (_y1_ - _calDate2_.[[Year]]) > 0, return *true*.
1. Else if _m1_ ≠ _calDate2_.[[Month]], then
1. If _sign_ × (_m1_ - _calDate2_.[[Month]]) > 0, return *true*.
1. Else if _d1_ ≠ _calDate2_.[[Day]], then
1. If _sign_ × (_d1_ - _calDate2_.[[Day]]) > 0, return *true*.
1. Return *false*.
</emu-alg>
</emu-clause>

<emu-clause id="sup-temporal-nonisodateadd" type="implementation-defined abstract operation">
<h1>
NonISODateAdd (
Expand All @@ -997,23 +1160,22 @@ 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_.
</dd>
</dl>
<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>
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateAdd for *"iso8601"*.</p>
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateadd"></emu-xref>.</p>
<p>It performs the following steps when called:</p>
<emu-alg>
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 _endOfMonth_.[[Day]] &lt; _baseDay_, then
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_.
</emu-alg>
Expand All @@ -1035,19 +1197,36 @@ contributors: Google, Ecma International
No fields larger than _largestUnit_ will be non-zero in the resulting Date Duration Record.
</dd>
</dl>
<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>
<p>All calendars follow the steps given here, which is a generalization of the precise algorithm specified in CalendarDateUntil for *"iso8601"*.</p>
<p>This definition supersedes the definition provided in <emu-xref href="#sec-temporal-nonisodateuntil"></emu-xref>.</p>
<p>It performs the following steps when called:</p>
<emu-alg>
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. 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_).
</emu-alg>
</emu-clause>

Expand Down Expand Up @@ -1087,8 +1266,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_ &le; _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_).
</emu-alg>
</emu-clause>

Expand Down