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,
- )
-
-
-
- 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
- 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.