Skip to content

Commit

Permalink
Normative: Call user code on relativeTo only when necessary in RoundD…
Browse files Browse the repository at this point in the history
…uration

relativeTo as a PlainDate is only needed when smallestUnit is year, month,
or week. relativeTo as a ZonedDateTime is used additionally when
smallestUnit is day. However, ToTemporalDate only needs to be called in
the former case. Since ToTemporalDate potentially calls user code,
rearrange some steps to make sure to call it only when necessary.

See: #2247
  • Loading branch information
ptomato committed Apr 27, 2023
1 parent f45e59c commit ceb60b3
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 26 deletions.
26 changes: 12 additions & 14 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5088,15 +5088,9 @@ export function RoundDuration(
relativeTo = undefined
) {
const TemporalDuration = GetIntrinsic('%Temporal.Duration%');
let calendar, zdtRelative;
if (relativeTo) {
if (IsTemporalZonedDateTime(relativeTo)) {
zdtRelative = relativeTo;
relativeTo = ToTemporalDate(relativeTo);
} else if (!IsTemporalDate(relativeTo)) {
throw new TypeError('starting point must be PlainDate or ZonedDateTime');
}
calendar = GetSlot(relativeTo, CALENDAR);

if ((unit === 'year' || unit === 'month' || unit === 'week') && !relativeTo) {
throw new RangeError(`A starting point is required for ${unit}s rounding`);
}

// First convert time units up to days, if rounding to days or higher units.
Expand All @@ -5105,8 +5099,8 @@ export function RoundDuration(
if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') {
nanoseconds = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0);
let intermediate;
if (zdtRelative) {
intermediate = MoveRelativeZonedDateTime(zdtRelative, years, months, weeks, days);
if (IsTemporalZonedDateTime(relativeTo)) {
intermediate = MoveRelativeZonedDateTime(relativeTo, years, months, weeks, days);
}
let deltaDays;
({ days: deltaDays, nanoseconds, dayLengthNs } = NanosecondsToDays(nanoseconds, intermediate));
Expand All @@ -5117,7 +5111,8 @@ export function RoundDuration(
let total;
switch (unit) {
case 'year': {
if (!calendar) throw new RangeError('A starting point is required for years rounding');
relativeTo = ToTemporalDate(relativeTo);
const calendar = GetSlot(relativeTo, CALENDAR);

// convert months and weeks to days by calculating difference(
// relativeTo + years, relativeTo + { years, months, weeks })
Expand Down Expand Up @@ -5161,7 +5156,8 @@ export function RoundDuration(
break;
}
case 'month': {
if (!calendar) throw new RangeError('A starting point is required for months rounding');
relativeTo = ToTemporalDate(relativeTo);
const calendar = GetSlot(relativeTo, CALENDAR);

// convert weeks to days by calculating difference(relativeTo +
// { years, months }, relativeTo + { years, months, weeks })
Expand Down Expand Up @@ -5196,7 +5192,9 @@ export function RoundDuration(
break;
}
case 'week': {
if (!calendar) throw new RangeError('A starting point is required for weeks rounding');
relativeTo = ToTemporalDate(relativeTo);
const calendar = GetSlot(relativeTo, CALENDAR);

// Weeks may be different lengths of days depending on the calendar,
// convert days to weeks in a loop as described above under 'years'.
const sign = MathSign(days);
Expand Down
20 changes: 8 additions & 12 deletions spec/duration.html
Original file line number Diff line number Diff line change
Expand Up @@ -1663,28 +1663,20 @@ <h1>
1. If _relativeTo_ is not present, set _relativeTo_ to *undefined*.
1. If _unit_ is *"year"*, *"month"*, or *"week"*, and _relativeTo_ is *undefined*, then
1. Throw a *RangeError* exception.
1. Let _zonedRelativeTo_ be *undefined*.
1. If _relativeTo_ is not *undefined*, then
1. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
1. Set _zonedRelativeTo_ to _relativeTo_.
1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_).
1. Else,
1. Assert: _relativeTo_ has an [[InitializedTemporalDate]] internal slot.
1. Let _calendar_ be _relativeTo_.[[Calendar]].
1. Else,
1. NOTE: _calendar_ will not be used below.
1. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
1. Let _nanoseconds_ be ! TotalDurationNanoseconds(0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, 0).
1. Let _intermediate_ be *undefined*.
1. If _zonedRelativeTo_ is not *undefined*, then
1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_).
1. If _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, then
1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_relativeTo_, _years_, _months_, _weeks_, _days_).
1. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
1. Set _days_ to _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]].
1. Set _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0.
1. Else,
1. Let _fractionalSeconds_ be _nanoseconds_ &times; 10<sup>-9</sup> + _microseconds_ &times; 10<sup>-6</sup> + _milliseconds_ &times; 10<sup>-3</sup> + _seconds_.
1. Let _remainder_ be *undefined*.
1. If _unit_ is *"year"*, then
1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_).
1. Let _calendar_ be _relativeTo_.[[Calendar]].
1. Let _yearsDuration_ be ! CreateTemporalDuration(_years_, 0, 0, 0, 0, 0, 0, 0, 0, 0).
1. If _calendar_ is an Object, then
1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*).
Expand Down Expand Up @@ -1717,6 +1709,8 @@ <h1>
1. Set _remainder_ to _fractionalYears_ - _years_.
1. Set _months_, _weeks_, and _days_ to 0.
1. Else if _unit_ is *"month"*, then
1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_).
1. Let _calendar_ be _relativeTo_.[[Calendar]].
1. Let _yearsMonths_ be ! CreateTemporalDuration(_years_, _months_, 0, 0, 0, 0, 0, 0, 0, 0).
1. If _calendar_ is an Object, then
1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*).
Expand Down Expand Up @@ -1744,6 +1738,8 @@ <h1>
1. Set _remainder_ to _fractionalMonths_ - _months_.
1. Set _weeks_ and _days_ to 0.
1. Else if _unit_ is *"week"*, then
1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_).
1. Let _calendar_ be _relativeTo_.[[Calendar]].
1. If _days_ &lt; 0, let _sign_ be -1; else, let _sign_ be 1.
1. Let _oneWeek_ be ! CreateTemporalDuration(0, 0, _sign_, 0, 0, 0, 0, 0, 0, 0).
1. If _calendar_ is an Object, then
Expand Down

0 comments on commit ceb60b3

Please sign in to comment.