diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 260995269b..cda983e7ae 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -13,6 +13,9 @@ import { MILLISECONDS, MICROSECONDS, NANOSECONDS, + CALENDAR, + INSTANT, + TIME_ZONE, CreateSlots, GetSlot, SetSlot @@ -614,17 +617,58 @@ export class Duration { ) { return 0; } - let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); - const shift1 = ES.CalculateOffsetShift(zonedRelativeTo, y1, mon1, w1, d1); - const shift2 = ES.CalculateOffsetShift(zonedRelativeTo, y2, mon2, w2, d2); - if (y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0) { - if (zonedRelativeTo) plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo); + const calendarUnitsPresent = y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0; + + if (zonedRelativeTo && (calendarUnitsPresent || d1 != 0 || d2 !== 0)) { + const instant = GetSlot(zonedRelativeTo, INSTANT); + const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE); + const calendar = GetSlot(zonedRelativeTo, CALENDAR); + const precalculatedDateTime = ES.GetPlainDateTimeFor(timeZone, instant, calendar); + + const after1 = ES.AddZonedDateTime( + instant, + timeZone, + calendar, + y1, + mon1, + w1, + d1, + h1, + min1, + s1, + ms1, + µs1, + ns1, + precalculatedDateTime + ); + const after2 = ES.AddZonedDateTime( + instant, + timeZone, + calendar, + y2, + mon2, + w2, + d2, + h2, + min2, + s2, + ms2, + µs2, + ns2, + precalculatedDateTime + ); + return ES.ComparisonResult(after1.minus(after2).toJSNumber()); + } + + if (calendarUnitsPresent) { + // plainRelativeTo may be undefined, and if so Unbalance will throw ({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', plainRelativeTo)); ({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', plainRelativeTo)); } - ns1 = ES.TotalDurationNanoseconds(d1, h1, min1, s1, ms1, µs1, ns1, shift1); - ns2 = ES.TotalDurationNanoseconds(d2, h2, min2, s2, ms2, µs2, ns2, shift2); + ns1 = ES.TotalDurationNanoseconds(d1, h1, min1, s1, ms1, µs1, ns1); + ns2 = ES.TotalDurationNanoseconds(d2, h2, min2, s2, ms2, µs2, ns2); return ES.ComparisonResult(ns1.minus(ns2).toJSNumber()); } } diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 353ffb7201..bf8f916461 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -2560,7 +2560,7 @@ export function TemporalDurationToString( ) { const sign = DurationSign(years, months, weeks, days, hours, minutes, seconds, ms, µs, ns); - let total = TotalDurationNanoseconds(0, 0, 0, seconds, ms, µs, ns, 0); + let total = TotalDurationNanoseconds(0, 0, 0, seconds, ms, µs, ns); ({ quotient: total, remainder: ns } = total.divmod(1000)); ({ quotient: total, remainder: µs } = total.divmod(1000)); ({ quotient: seconds, remainder: ms } = total.divmod(1000)); @@ -3233,17 +3233,7 @@ export function BalanceTime(hour, minute, second, millisecond, microsecond, nano }; } -export function TotalDurationNanoseconds( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - offsetShift -) { - if (days !== 0) nanoseconds = bigInt(nanoseconds).subtract(offsetShift); +export function TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) { hours = bigInt(hours).add(bigInt(days).multiply(24)); minutes = bigInt(minutes).add(hours.multiply(60)); seconds = bigInt(seconds).add(minutes.multiply(60)); @@ -3394,7 +3384,7 @@ export function BalancePossiblyInfiniteTimeDuration( nanoseconds, largestUnit ) { - nanoseconds = TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); + nanoseconds = TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); const sign = nanoseconds.lesser(0) ? -1 : 1; nanoseconds = nanoseconds.abs(); @@ -3818,21 +3808,6 @@ export function BalanceDateDurationRelative(years, months, weeks, days, largestU }; } -export function CalculateOffsetShift(relativeTo, y, mon, w, d) { - if (IsTemporalZonedDateTime(relativeTo)) { - const instant = GetSlot(relativeTo, INSTANT); - const timeZone = GetSlot(relativeTo, TIME_ZONE); - const calendar = GetSlot(relativeTo, CALENDAR); - const offsetBefore = GetOffsetNanosecondsFor(timeZone, instant); - const after = AddZonedDateTime(instant, timeZone, calendar, y, mon, w, d, 0, 0, 0, 0, 0, 0); - const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); - const instantAfter = new TemporalInstant(after); - const offsetAfter = GetOffsetNanosecondsFor(timeZone, instantAfter); - return offsetAfter - offsetBefore; - } - return 0; -} - export function CreateNegatedTemporalDuration(duration) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); return new TemporalDuration( @@ -5355,16 +5330,7 @@ export function AdjustRoundedDurationDays( // duration, there's no way for another full day to come from the next // round of rounding. And if it were possible (e.g. contrived calendar // with 30-minute-long "days") then it'd risk an infinite loop. - let timeRemainderNs = TotalDurationNanoseconds( - 0, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - 0 - ); + let timeRemainderNs = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); const direction = MathSign(timeRemainderNs.toJSNumber()); const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE); @@ -5484,7 +5450,7 @@ export function RoundDuration( // If rounding relative to a ZonedDateTime, then some days may not be 24h. let dayLengthNs; if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') { - nanoseconds = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); + nanoseconds = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); let deltaDays; if (zonedRelativeTo) { const intermediate = MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days); diff --git a/spec/duration.html b/spec/duration.html index 879b6a3997..4d62fbc302 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -99,10 +99,19 @@

Temporal.Duration.compare ( _one_, _two_ [ , _options_ ] )

1. Let _relativeToRecord_ be ? ToRelativeTemporalObject(_options_). 1. Let _zonedRelativeTo_ be _relativeToRecord_.[[ZonedRelativeTo]]. 1. Let _plainRelativeTo_ be _relativeToRecord_.[[PlainRelativeTo]]. - 1. Let _shift1_ be ? CalculateOffsetShift(_zonedRelativeTo_, _one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]]). - 1. Let _shift2_ be ? CalculateOffsetShift(_zonedRelativeTo_, _two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]]). - 1. If _one_.[[Years]] ≠ 0, or _two_.[[Years]] ≠ 0, or _one_.[[Months]] ≠ 0, or _two_.[[Months]] ≠ 0, or _one_.[[Weeks]] ≠ 0, or _two_.[[Weeks]] ≠ 0, then - 1. If _zonedRelativeTo_ is not *undefined*, set _plainRelativeTo_ to ? ToTemporalDate(_zonedRelativeTo_). + 1. Let _calendarUnitsPresent_ be *false*. + 1. If _one_.[[Years]] ≠ 0, or _two_.[[Years]] ≠ 0, or _one_.[[Months]] ≠ 0, or _two_.[[Months]] ≠ 0, or _one_.[[Weeks]] ≠ 0, or _two_.[[Weeks]] ≠ 0, set _calendarUnitsPresent_ to *true*. + 1. If _zonedRelativeTo_ is not *undefined*, and either _calendarUnitsPresent_ is *true*, or _one_.[[Days]] ≠ 0, or _two_.[[Days]] ≠ 0, then + 1. Let _timeZone_ be _zonedRelativeTo_.[[TimeZone]]. + 1. Let _calendar_ be _zonedRelativeTo_.[[Calendar]]. + 1. Let _instant_ be ! CreateTemporalInstant(_zonedRelativeTo_.[[Nanoseconds]]). + 1. Let _precalculatedDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). + 1. Let _after1_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]], _one_.[[Hours]], _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]], _precalculatedDateTime_). + 1. Let _after2_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], _two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]], _precalculatedDateTime_). + 1. If _after1_ > _after2_, return *1*𝔽. + 1. If _after1_ < _after2_, return *-1*𝔽. + 1. Return *+0*𝔽. + 1. If _calendarUnitsPresent_ is *true*, then 1. Let _unbalanceResult1_ be ? UnbalanceDateDurationRelative(_one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]], *"day"*, _plainRelativeTo_). 1. Let _unbalanceResult2_ be ? UnbalanceDateDurationRelative(_two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], *"day"*, _plainRelativeTo_). 1. Let _days1_ be _unbalanceResult1_.[[Days]]. @@ -110,8 +119,8 @@

Temporal.Duration.compare ( _one_, _two_ [ , _options_ ] )

1. Else, 1. Let _days1_ be _one_.[[Days]]. 1. Let _days2_ be _two_.[[Days]]. - 1. Let _ns1_ be ! TotalDurationNanoseconds(_days1_, _one_.[[Hours]], _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]], _shift1_). - 1. Let _ns2_ be ! TotalDurationNanoseconds(_days2_, _two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]], _shift2_). + 1. Let _ns1_ be TotalDurationNanoseconds(_days1_, _one_.[[Hours]], _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]]). + 1. Let _ns2_ be TotalDurationNanoseconds(_days2_, _two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]]). 1. If _ns1_ > _ns2_, return *1*𝔽. 1. If _ns1_ < _ns2_, return *-1*𝔽. 1. Return *+0*𝔽. @@ -1136,31 +1145,6 @@

- -

- CalculateOffsetShift ( - _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, - _y_: an integer, - _mon_: an integer, - _w_: an integer, - _d_: an integer, - ) -

-
-
description
-
It returns an integer difference in nanoseconds between the time zone offset at the time of _relativeTo_, and the time zone offset at the time of _relativeTo_ plus the given duration.
-
- - 1. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, return 0. - 1. Let _instant_ be ! CreateTemporalInstant(_relativeTo_.[[Nanoseconds]]). - 1. Let _offsetBefore_ be ? GetOffsetNanosecondsFor(_relativeTo_.[[TimeZone]], _instant_). - 1. Let _after_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], _y_, _mon_, _w_, _d_, 0, 0, 0, 0, 0, 0). - 1. Let _instantAfter_ be ! CreateTemporalInstant(_after_). - 1. Let _offsetAfter_ be ? GetOffsetNanosecondsFor(_relativeTo_.[[TimeZone]], _instantAfter_). - 1. Return _offsetAfter_ - _offsetBefore_. - -
-

TotalDurationNanoseconds ( @@ -1171,16 +1155,16 @@

_milliseconds_: an integer, _microseconds_: an integer, _nanoseconds_: an integer, - _offsetShift_: an integer, - ) + ): an integer

description
-
It computes an integer number of nanoseconds from the given units, applying a given time zone offset shift in nanoseconds when converting from days to hours.
+
+ It computes an integer number of nanoseconds from the given units. + Note that this operation should not be used with _days_ ≠ 0 if the calculation is relative to a Temporal.ZonedDateTime. +
- 1. If _days_ ≠ 0, then - 1. Set _nanoseconds_ to _nanoseconds_ - _offsetShift_. 1. Set _hours_ to _hours_ + _days_ × 24. 1. Set _minutes_ to _minutes_ + _hours_ × 60. 1. Set _seconds_ to _seconds_ + _minutes_ × 60. @@ -1234,7 +1218,7 @@

It converts the time units of a duration into a form where lower units are converted into higher units as much as possible, up to _largestUnit_. If the Number value for any unit is infinite, it returns a special value indicating the direction of overflow.
- 1. Set _nanoseconds_ to ! TotalDurationNanoseconds(_days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, 0). + 1. Set _nanoseconds_ to TotalDurationNanoseconds(_days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). 1. Set _days_, _hours_, _minutes_, _seconds_, _milliseconds_, and _microseconds_ to 0. 1. If _nanoseconds_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Set _nanoseconds_ to abs(_nanoseconds_). @@ -1730,7 +1714,7 @@

1. If _unit_ is *"year"*, *"month"*, or *"week"*, and _plainRelativeTo_ is *undefined*, then 1. Throw a *RangeError* exception. 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 _nanoseconds_ be TotalDurationNanoseconds(0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). 1. If _zonedRelativeTo_ is not *undefined*, then 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_). 1. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). @@ -1893,7 +1877,7 @@

1. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then 1. Return ! CreateDurationRecord(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Let _timeRemainderNs_ be ! TotalDurationNanoseconds(0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, 0). + 1. Let _timeRemainderNs_ be TotalDurationNanoseconds(0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). 1. If _timeRemainderNs_ = 0, let _direction_ be 0. 1. Else if _timeRemainderNs_ < 0, let _direction_ be -1. 1. Else, let _direction_ be 1.