From ca447f67f63f94cdf8685ebe3e22c209d8a2f639 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 2 Mar 2023 18:18:17 -0800 Subject: [PATCH 1/9] Normative: Avoid calling user code in no-op round operations If the given rounding options specify a smallest unit of nanoseconds and a rounding increment of 1, then the rounding operation is a no-op. In that case, return a copy of the duration, without actually performing the rounding operation. This applies to all round(), until(), and since() methods. In the case of Duration.p.round(), other conditions must be fulfilled as well for the operation to be a no-op: no balancing must take place. In the case of Instant.p.round(), PlainDate.p.since(), PlainDate.p.until(), PlainDateTime.p.round(), PlainDateTime.p.since(), PlainDateTime.p.until(), PlainTime.p.round(), PlainTime.p.since(), and PlainTime.p.until(), the change is not observable, so implementors are free to make this optimization anyway. This optimization was already the case in PlainDate.p.since/until() and PlainYearMonth.p.since/until(), so this makes the other since/until() methods consistent with those. --- polyfill/lib/duration.mjs | 20 ++++- polyfill/lib/ecmascript.mjs | 159 +++++++++++++++++---------------- polyfill/lib/plaindatetime.mjs | 14 +++ polyfill/lib/zoneddatetime.mjs | 8 ++ spec/duration.html | 10 ++- spec/instant.html | 2 + spec/plaindatetime.html | 4 + spec/plaintime.html | 5 +- spec/zoneddatetime.html | 4 + 9 files changed, 145 insertions(+), 81 deletions(-) diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 698da89d73..1145730fdb 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -223,7 +223,7 @@ export class Duration { let microseconds = GetSlot(this, MICROSECONDS); let nanoseconds = GetSlot(this, NANOSECONDS); - let defaultLargestUnit = ES.DefaultTemporalLargestUnit( + const existingLargestUnit = ES.DefaultTemporalLargestUnit( years, months, weeks, @@ -254,7 +254,7 @@ export class Duration { smallestUnitPresent = false; smallestUnit = 'nanosecond'; } - defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit); + const defaultLargestUnit = ES.LargerOfTwoTemporalUnits(existingLargestUnit, smallestUnit); let largestUnitPresent = true; if (!largestUnit) { largestUnitPresent = false; @@ -279,6 +279,22 @@ export class Duration { const maximum = maximumIncrements[smallestUnit]; if (maximum !== undefined) ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false); + const roundingGranularityIsNoop = smallestUnit === 'nanosecond' && roundingIncrement === 1; + const balancingRequested = largestUnit !== existingLargestUnit; + const calendarUnitsPresent = years !== 0 || months !== 0 || weeks !== 0; + const timeUnitsOverflowWillOccur = + minutes >= 60 || seconds >= 60 || milliseconds >= 1000 || microseconds >= 1000 || nanoseconds >= 1000; + const hoursToDaysConversionMayOccur = (days !== 0 && ES.IsTemporalZonedDateTime(relativeTo)) || hours >= 24; + if ( + roundingGranularityIsNoop && + !balancingRequested && + !calendarUnitsPresent && + !timeUnitsOverflowWillOccur && + !hoursToDaysConversionMayOccur + ) { + return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + } + ({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative( years, months, diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 63e19822a5..bdad7ecfeb 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -4045,21 +4045,23 @@ export function DifferenceInstant(ns1, ns2, increment, smallestUnit, largestUnit let milliseconds = diff.divide(1e6).mod(1e3).toJSNumber(); let seconds = diff.divide(1e9).toJSNumber(); - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( - 0, - 0, - 0, - 0, - 0, - 0, - seconds, - milliseconds, - microseconds, - nanoseconds, - increment, - smallestUnit, - roundingMode - )); + if (smallestUnit !== 'nanosecond' || increment !== 1) { + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + 0, + 0, + 0, + 0, + 0, + 0, + seconds, + milliseconds, + microseconds, + nanoseconds, + increment, + smallestUnit, + roundingMode + )); + } return BalanceTimeDuration(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit); } @@ -4359,33 +4361,35 @@ export function DifferenceTemporalPlainDateTime(operation, plainDateTime, other, resolvedOptions ); - const relativeTo = TemporalDateTimeToDate(plainDateTime); - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( - years, - months, - weeks, - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - settings.roundingIncrement, - settings.smallestUnit, - settings.roundingMode, - relativeTo - )); - ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - settings.largestUnit - )); + if (settings.smallestUnit !== 'nanosecond' || settings.roundingIncrement !== 1) { + const relativeTo = TemporalDateTimeToDate(plainDateTime); + ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + years, + months, + weeks, + days, + hours, + minutes, + seconds, + milliseconds, + microseconds, + nanoseconds, + settings.roundingIncrement, + settings.smallestUnit, + settings.roundingMode, + relativeTo + )); + ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( + days, + hours, + minutes, + seconds, + milliseconds, + microseconds, + nanoseconds, + settings.largestUnit + )); + } const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration( @@ -4423,21 +4427,23 @@ export function DifferenceTemporalPlainTime(operation, plainTime, other, options GetSlot(other, ISO_MICROSECOND), GetSlot(other, ISO_NANOSECOND) ); - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( - 0, - 0, - 0, - 0, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - settings.roundingIncrement, - settings.smallestUnit, - settings.roundingMode - )); + if (settings.smallestUnit !== 'nanosecond' || settings.roundingIncrement !== 1) { + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + 0, + 0, + 0, + 0, + hours, + minutes, + seconds, + milliseconds, + microseconds, + nanoseconds, + settings.roundingIncrement, + settings.smallestUnit, + settings.roundingMode + )); + } ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( 0, hours, @@ -4550,24 +4556,9 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, } ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, settings.largestUnit, resolvedOptions)); - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( - years, - months, - weeks, - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - settings.roundingIncrement, - settings.smallestUnit, - settings.roundingMode, - zonedDateTime - )); - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - AdjustRoundedDurationDays( + + if (settings.smallestUnit !== 'nanosecond' || settings.roundingIncrement !== 1) { + ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( years, months, weeks, @@ -4583,6 +4574,24 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, settings.roundingMode, zonedDateTime )); + ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = + AdjustRoundedDurationDays( + years, + months, + weeks, + days, + hours, + minutes, + seconds, + milliseconds, + microseconds, + nanoseconds, + settings.roundingIncrement, + settings.smallestUnit, + settings.roundingMode, + zonedDateTime + )); + } } const Duration = GetIntrinsic('%Temporal.Duration%'); diff --git a/polyfill/lib/plaindatetime.mjs b/polyfill/lib/plaindatetime.mjs index 9648bedaf2..72aa4c6a83 100644 --- a/polyfill/lib/plaindatetime.mjs +++ b/polyfill/lib/plaindatetime.mjs @@ -310,6 +310,20 @@ export class PlainDateTime { let millisecond = GetSlot(this, ISO_MILLISECOND); let microsecond = GetSlot(this, ISO_MICROSECOND); let nanosecond = GetSlot(this, ISO_NANOSECOND); + if (roundingIncrement === 1 && smallestUnit === 'nanosecond') { + return ES.CreateTemporalDateTime( + year, + month, + day, + hour, + minute, + second, + millisecond, + microsecond, + nanosecond, + GetSlot(this, CALENDAR) + ); + } ({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.RoundISODateTime( year, month, diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index fedbd4785e..498f6114bc 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -364,6 +364,14 @@ export class ZonedDateTime { const inclusive = maximum === 1; ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, inclusive); + if (smallestUnit === 'nanosecond' && roundingIncrement === 1) { + return ES.CreateTemporalZonedDateTime( + GetSlot(this, EPOCHNANOSECONDS), + GetSlot(this, TIME_ZONE), + GetSlot(this, CALENDAR) + ); + } + // first, round the underlying DateTime fields const timeZone = GetSlot(this, TIME_ZONE); const offsetNs = ES.GetOffsetNanosecondsFor(timeZone, GetSlot(this, INSTANT)); diff --git a/spec/duration.html b/spec/duration.html index f09027c90c..7f4594c5b9 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -421,8 +421,8 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. If _smallestUnit_ is *undefined*, then 1. Set _smallestUnitPresent_ to *false*. 1. Set _smallestUnit_ to *"nanosecond"*. - 1. Let _defaultLargestUnit_ be ! DefaultTemporalLargestUnit(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]]). - 1. Set _defaultLargestUnit_ to ! LargerOfTwoTemporalUnits(_defaultLargestUnit_, _smallestUnit_). + 1. Let _existingLargestUnit_ be ! DefaultTemporalLargestUnit(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]]). + 1. Let _defaultLargestUnit_ be ! LargerOfTwoTemporalUnits(_existingLargestUnit_, _smallestUnit_). 1. If _largestUnit_ is *undefined*, then 1. Set _largestUnitPresent_ to *false*. 1. Set _largestUnit_ to _defaultLargestUnit_. @@ -433,6 +433,12 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. If LargerOfTwoTemporalUnits(_largestUnit_, _smallestUnit_) is not _largestUnit_, throw a *RangeError* exception. 1. Let _maximum_ be ! MaximumTemporalDurationRoundingIncrement(_smallestUnit_). 1. If _maximum_ is not *undefined*, perform ? ValidateTemporalRoundingIncrement(_roundingIncrement_, _maximum_, *false*). + 1. Let _hoursToDaysConversionMayOccur_ be *false*. + 1. If _duration_.[[Days]] ≠ 0 and _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, set _hoursToDaysConversionMayOccur_ to *true*. + 1. Else if _duration_.[[Hours]] ≥ 24, set _hoursToDaysConversionMayOccur_ to *true*. + 1. If _smallestUnit_ is *"nanosecond"*, and _roundingIncrement_ = 1, and _largestUnit_ is _existingLargestUnit_, and _duration_.[[Years]] = 0, and _duration_.[[Months]] = 0, and _duration_.[[Weeks]] = 0, and _hoursToDaysConversionMayOccur_ is *false*, and _duration_.[[Minutes]] < 60, and _duration_.[[Seconds]] < 60, and _duration_.[[Milliseconds]] < 1000, and _duration_.[[Microseconds]] < 1000, and _duration_.[[Nanoseconds]] < 1000, then + 1. NOTE: The above conditions mean that the operation will have no effect: the smallest unit and rounding increment will leave the total duration unchanged, and it can be determined without calling a calendar or time zone method that no balancing will take place. + 1. Return ! CreateTemporalDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_). 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. diff --git a/spec/instant.html b/spec/instant.html index 2c70abf046..967bcbfcc6 100644 --- a/spec/instant.html +++ b/spec/instant.html @@ -596,6 +596,8 @@

1. Let _microseconds_ be remainder(truncate(_difference_ / 1000), 1000). 1. Let _milliseconds_ be remainder(truncate(_difference_ / 106), 1000). 1. Let _seconds_ be truncate(_difference_ / 109). + 1. If _smallestUnit_ is *"nanosecond"* and _roundingIncrement_ is 1, then + 1. Return ! BalanceTimeDuration(0, 0, 0, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, _largestUnit_). 1. Let _roundResult_ be ! RoundDuration(0, 0, 0, 0, 0, 0, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_, _roundingIncrement_, _smallestUnit_, _roundingMode_). 1. Assert: _roundResult_.[[Days]] is 0. 1. Return ! BalanceTimeDuration(0, _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_). diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html index eaa9c80d82..107d647722 100644 --- a/spec/plaindatetime.html +++ b/spec/plaindatetime.html @@ -537,6 +537,8 @@

Temporal.PlainDateTime.prototype.round ( _roundTo_ )

1. Assert: _maximum_ is not *undefined*. 1. Let _inclusive_ be *false*. 1. Perform ? ValidateTemporalRoundingIncrement(_roundingIncrement_, _maximum_, _inclusive_). + 1. If _smallestUnit_ is *"nanosecond"* and _roundingIncrement_ is 1, then + 1. Return ! CreateTemporalDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _dateTime_.[[Calendar]]). 1. Let _result_ be RoundISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _roundingIncrement_, _smallestUnit_, _roundingMode_). 1. Return ? CreateTemporalDateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _dateTime_.[[Calendar]]). @@ -1280,6 +1282,8 @@

1. Let _resolvedOptions_ be ? SnapshotOwnProperties(? GetOptionsObject(_options_), *null*). 1. Let _settings_ be ? GetDifferenceSettings(_operation_, _resolvedOptions_, ~datetime~, « », *"nanosecond"*, *"day"*). 1. Let _diff_ be ? DifferenceISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _other_.[[ISOYear]], _other_.[[ISOMonth]], _other_.[[ISODay]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _dateTime_.[[Calendar]], _settings_.[[LargestUnit]], _resolvedOptions_). + 1. If _settings_.[[SmallestUnit]] is *"nanosecond"* and _settings_.[[RoundingIncrement]] is 1, then + 1. Return ! CreateTemporalDuration(_sign_ × _diff_.[[Years]], _sign_ × _diff_.[[Months]], _sign_ × _diff_.[[Weeks]], _sign_ × _diff_.[[Days]], _sign_ × _diff_.[[Hours]], _sign_ × _diff_.[[Minutes]], _sign_ × _diff_.[[Seconds]], _sign_ × _diff_.[[Milliseconds]], _sign_ × _diff_.[[Microseconds]], _sign_ × _diff_.[[Nanoseconds]]). 1. Let _relativeTo_ be ! CreateTemporalDate(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[Calendar]]). 1. Let _roundRecord_ be ? RoundDuration(_diff_.[[Years]], _diff_.[[Months]], _diff_.[[Weeks]], _diff_.[[Days]], _diff_.[[Hours]], _diff_.[[Minutes]], _diff_.[[Seconds]], _diff_.[[Milliseconds]], _diff_.[[Microseconds]], _diff_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _relativeTo_). 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. diff --git a/spec/plaintime.html b/spec/plaintime.html index 577ce215af..481a0faab4 100644 --- a/spec/plaintime.html +++ b/spec/plaintime.html @@ -1018,8 +1018,9 @@

1. Let _resolvedOptions_ be ? SnapshotOwnProperties(? GetOptionsObject(_options_), *null*). 1. Let _settings_ be ? GetDifferenceSettings(_operation_, _resolvedOptions_, ~time~, « », *"nanosecond"*, *"hour"*). 1. Let _result_ be ! DifferenceTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]]). - 1. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]]). - 1. Set _result_ to _roundRecord_.[[DurationRecord]]. + 1. If _settings_.[[SmallestUnit]] is not *"nanosecond"* or _settings_.[[RoundingIncrement]] ≠ 1, then + 1. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]]). + 1. Set _result_ to _roundRecord_.[[DurationRecord]]. 1. Set _result_ to ! BalanceTimeDuration(0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]], _settings_.[[LargestUnit]]). 1. Return ! CreateTemporalDuration(0, 0, 0, 0, _sign_ × _result_.[[Hours]], _sign_ × _result_.[[Minutes]], _sign_ × _result_.[[Seconds]], _sign_ × _result_.[[Milliseconds]], _sign_ × _result_.[[Microseconds]], _sign_ × _result_.[[Nanoseconds]]). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index 39be0dca50..8cf1e2ea3c 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -758,6 +758,8 @@

Temporal.ZonedDateTime.prototype.round ( _roundTo_ )

1. Assert: _maximum_ is not *undefined*. 1. Let _inclusive_ be *false*. 1. Perform ? ValidateTemporalRoundingIncrement(_roundingIncrement_, _maximum_, _inclusive_). + 1. If _smallestUnit_ is *"nanosecond"* and _roundingIncrement_ is 1, then + 1. Return ! CreateTemporalZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]]). 1. Let _timeZone_ be _zonedDateTime_.[[TimeZone]]. 1. Let _instant_ be ! CreateTemporalInstant(_zonedDateTime_.[[Nanoseconds]]). 1. Let _calendar_ be _zonedDateTime_.[[Calendar]]. @@ -1432,6 +1434,8 @@

1. If ? TimeZoneEquals(_zonedDateTime_.[[TimeZone]], _other_.[[TimeZone]]) is *false*, then 1. Throw a *RangeError* exception. 1. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _settings_.[[LargestUnit]], _resolvedOptions_). + 1. If _settings_.[[SmallestUnit]] is *"nanosecond"* and _settings_.[[RoundingIncrement]] is 1, then + 1. Return ! CreateTemporalDuration(_sign_ × _difference_.[[Years]], _sign_ × _difference_.[[Months]], _sign_ × _difference_.[[Weeks]], _sign_ × _difference_.[[Days]], _sign_ × _difference_.[[Hours]], _sign_ × _difference_.[[Minutes]], _sign_ × _difference_.[[Seconds]], _sign_ × _difference_.[[Milliseconds]], _sign_ × _difference_.[[Microseconds]], _sign_ × _difference_.[[Nanoseconds]]). 1. Let _roundRecord_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_). 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. 1. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_). From bc1307ab0f010d3afc896f621e7fabbbc0e8c779 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 3 Mar 2023 15:19:45 -0800 Subject: [PATCH 2/9] Normative: Call user code on relativeTo only when necessary in RoundDuration 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 --- polyfill/lib/ecmascript.mjs | 26 ++++++++++++-------------- spec/duration.html | 20 ++++++++------------ 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index bdad7ecfeb..f73f7f6feb 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -5433,15 +5433,9 @@ export function RoundDuration( relativeTo = undefined ) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); - let calendar, zonedRelativeTo; - if (relativeTo) { - if (IsTemporalZonedDateTime(relativeTo)) { - zonedRelativeTo = relativeTo; - relativeTo = ToTemporalDate(relativeTo); - } else if (!IsTemporalDate(relativeTo)) { - throw new Error('assertion failure in RoundDuration: _relativeTo_ must be PlainDate'); - } - 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. @@ -5450,8 +5444,8 @@ export function RoundDuration( if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') { nanoseconds = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); let deltaDays; - if (zonedRelativeTo) { - const intermediate = MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days); + if (IsTemporalZonedDateTime(relativeTo)) { + const intermediate = MoveRelativeZonedDateTime(relativeTo, years, months, weeks, days); ({ days: deltaDays, nanoseconds, dayLengthNs } = NanosecondsToDays(nanoseconds, intermediate)); } else { ({ quotient: deltaDays, remainder: nanoseconds } = nanoseconds.divmod(DAY_NANOS)); @@ -5465,7 +5459,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 }) @@ -5508,7 +5503,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 }) @@ -5543,7 +5539,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); diff --git a/spec/duration.html b/spec/duration.html index 7f4594c5b9..83d2ac6a77 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -1710,20 +1710,10 @@

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. 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. Let _fractionalDays_ be _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]]. 1. Else, @@ -1735,6 +1725,8 @@

1. Assert: _fractionalDays_ is not used below. 1. Let _total_ be ~unset~. 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"*). @@ -1767,6 +1759,8 @@

1. Set _total_ to _fractionalYears_. 1. Set _months_ and _weeks_ 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"*). @@ -1794,6 +1788,8 @@

1. Set _total_ to _fractionalMonths_. 1. Set _weeks_ to 0. 1. Else if _unit_ is *"week"*, then + 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). + 1. Let _calendar_ be _relativeTo_.[[Calendar]]. 1. If _fractionalDays_ < 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 From a203d8a22ad6ab4d303f567e4a6ce828a0657904 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 3 Mar 2023 15:34:14 -0800 Subject: [PATCH 3/9] Normative: Don't convert relativeTo to PlainDate twice in Duration.compare We call UnbalanceDurationRelative twice with the same relativeTo object. UnbalanceDurationRelative only uses plain or undefined relativeTo, not zoned, so it will convert it with ToTemporalDate. No need to do this twice; pre-convert it before the first call. See: #2247 --- polyfill/lib/duration.mjs | 3 ++- spec/duration.html | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 1145730fdb..eff42aada6 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -548,7 +548,7 @@ export class Duration { one = ES.ToTemporalDuration(one); two = ES.ToTemporalDuration(two); options = ES.GetOptionsObject(options); - const relativeTo = ES.ToRelativeTemporalObject(options); + let relativeTo = ES.ToRelativeTemporalObject(options); const y1 = GetSlot(one, YEARS); const mon1 = GetSlot(one, MONTHS); const w1 = GetSlot(one, WEEKS); @@ -572,6 +572,7 @@ export class Duration { const shift1 = ES.CalculateOffsetShift(relativeTo, y1, mon1, w1, d1); const shift2 = ES.CalculateOffsetShift(relativeTo, y2, mon2, w2, d2); if (y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0) { + if (ES.IsTemporalZonedDateTime(relativeTo)) relativeTo = ES.ToTemporalDate(relativeTo); ({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', relativeTo)); ({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', relativeTo)); } diff --git a/spec/duration.html b/spec/duration.html index 83d2ac6a77..e1983e3885 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -98,6 +98,7 @@

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

1. Let _shift1_ be ? CalculateOffsetShift(_relativeTo_, _one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]]). 1. Let _shift2_ be ? CalculateOffsetShift(_relativeTo_, _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 _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, set _relativeTo_ to ? ToTemporalDate(_relativeTo_). 1. Let _unbalanceResult1_ be ? UnbalanceDateDurationRelative(_one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]], *"day"*, _relativeTo_). 1. Let _unbalanceResult2_ be ? UnbalanceDateDurationRelative(_two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], *"day"*, _relativeTo_). 1. Let _days1_ be _unbalanceResult1_.[[Days]]. From d9f043a64829bc7c95552e3c11687ed8658ceb31 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 7 Mar 2023 14:31:47 -0800 Subject: [PATCH 4/9] Normative: Separate zoned and plain operations in RoundDuration This converts a ZonedDateTime relativeTo into a PlainDateTime relativeTo only when necessary, only after potentially throwing other errors, and only once. Previously, it could be converted up to a few separate times in each operation, such as UnbalanceDurationRelative, RoundDuration, and BalanceDurationRelative. Since the conversion is user-visible, we don't want to perform it when not necessary or perform it more times than necessary. Closes: #2247 Closes: #2529 --- polyfill/lib/duration.mjs | 69 ++++++--- polyfill/lib/ecmascript.mjs | 274 +++++++++++++++++++++++------------ polyfill/test/ecmascript.mjs | 90 ++++++++++++ spec/abstractops.html | 23 +-- spec/duration.html | 229 +++++++++++++++-------------- spec/zoneddatetime.html | 12 +- 6 files changed, 467 insertions(+), 230 deletions(-) diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index eff42aada6..27e640a7db 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -244,7 +244,7 @@ export class Duration { } let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']); - let relativeTo = ES.ToRelativeTemporalObject(roundTo); + let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(roundTo); const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo); const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); let smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'datetime', undefined); @@ -284,7 +284,7 @@ export class Duration { const calendarUnitsPresent = years !== 0 || months !== 0 || weeks !== 0; const timeUnitsOverflowWillOccur = minutes >= 60 || seconds >= 60 || milliseconds >= 1000 || microseconds >= 1000 || nanoseconds >= 1000; - const hoursToDaysConversionMayOccur = (days !== 0 && ES.IsTemporalZonedDateTime(relativeTo)) || hours >= 24; + const hoursToDaysConversionMayOccur = (days !== 0 && zonedRelativeTo) || hours >= 24; if ( roundingGranularityIsNoop && !balancingRequested && @@ -295,13 +295,27 @@ export class Duration { return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); } + const plainRelativeToWillBeUsed = + smallestUnit === 'year' || + smallestUnit === 'month' || + smallestUnit === 'week' || + years !== 0 || + months !== 0 || + weeks !== 0 || + days !== 0; + if (zonedRelativeTo && plainRelativeToWillBeUsed) { + // Convert a ZonedDateTime relativeTo to PlainDate only if needed in one + // of the operations below, because the conversion is user visible + plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo); + } + ({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative( years, months, weeks, days, largestUnit, - relativeTo + plainRelativeTo )); ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.RoundDuration( @@ -318,9 +332,10 @@ export class Duration { roundingIncrement, smallestUnit, roundingMode, - relativeTo + plainRelativeTo, + zonedRelativeTo )); - if (ES.IsTemporalZonedDateTime(relativeTo)) { + if (zonedRelativeTo) { ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.AdjustRoundedDurationDays( years, @@ -336,7 +351,7 @@ export class Duration { roundingIncrement, smallestUnit, roundingMode, - relativeTo + zonedRelativeTo )); ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDurationRelative( days, @@ -347,7 +362,7 @@ export class Duration { microseconds, nanoseconds, largestUnit, - relativeTo + zonedRelativeTo )); } else { ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration( @@ -367,7 +382,7 @@ export class Duration { weeks, days, largestUnit, - relativeTo + plainRelativeTo )); return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); @@ -393,15 +408,30 @@ export class Duration { } else { totalOf = ES.GetOptionsObject(totalOf); } - const relativeTo = ES.ToRelativeTemporalObject(totalOf); + let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(totalOf); const unit = ES.GetTemporalUnit(totalOf, 'unit', 'datetime', ES.REQUIRED); + const plainRelativeToWillBeUsed = + unit === 'year' || unit === 'month' || unit === 'week' || years !== 0 || months !== 0 || weeks !== 0; + if (zonedRelativeTo !== undefined && plainRelativeToWillBeUsed) { + // Convert a ZonedDateTime relativeTo to PlainDate only if needed in one + // of the operations below, because the conversion is user visible + plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo); + } + // Convert larger units down to days - ({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(years, months, weeks, days, unit, relativeTo)); + ({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative( + years, + months, + weeks, + days, + unit, + plainRelativeTo + )); // If the unit we're totalling is smaller than `days`, convert days down to that unit. let balanceResult; - if (ES.IsTemporalZonedDateTime(relativeTo)) { - const intermediate = ES.MoveRelativeZonedDateTime(relativeTo, years, months, weeks, 0); + if (zonedRelativeTo) { + const intermediate = ES.MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, 0); balanceResult = ES.BalancePossiblyInfiniteTimeDurationRelative( days, hours, @@ -446,7 +476,8 @@ export class Duration { 1, unit, 'trunc', - relativeTo + plainRelativeTo, + zonedRelativeTo ); return total; } @@ -548,7 +579,7 @@ export class Duration { one = ES.ToTemporalDuration(one); two = ES.ToTemporalDuration(two); options = ES.GetOptionsObject(options); - let relativeTo = ES.ToRelativeTemporalObject(options); + let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); const y1 = GetSlot(one, YEARS); const mon1 = GetSlot(one, MONTHS); const w1 = GetSlot(one, WEEKS); @@ -569,12 +600,12 @@ export class Duration { const ms2 = GetSlot(two, MILLISECONDS); const µs2 = GetSlot(two, MICROSECONDS); let ns2 = GetSlot(two, NANOSECONDS); - const shift1 = ES.CalculateOffsetShift(relativeTo, y1, mon1, w1, d1); - const shift2 = ES.CalculateOffsetShift(relativeTo, y2, mon2, w2, d2); + 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 (ES.IsTemporalZonedDateTime(relativeTo)) relativeTo = ES.ToTemporalDate(relativeTo); - ({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', relativeTo)); - ({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', relativeTo)); + if (zonedRelativeTo) plainRelativeTo = ES.ToTemporalDate(zonedRelativeTo); + ({ 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); diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index f73f7f6feb..9c17e94113 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -973,15 +973,21 @@ export function GetTemporalUnit(options, key, unitGroup, requiredOrDefault, extr } export function ToRelativeTemporalObject(options) { + // returns: { + // plainRelativeTo: Temporal.PlainDate | undefined + // zonedRelativeTo: Temporal.ZonedDateTime | undefined + // } + // plainRelativeTo and zonedRelativeTo are mutually exclusive. const relativeTo = options.relativeTo; - if (relativeTo === undefined) return relativeTo; + if (relativeTo === undefined) return {}; let offsetBehaviour = 'option'; let matchMinutes = false; let year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, timeZone, offset; if (Type(relativeTo) === 'Object') { - if (IsTemporalZonedDateTime(relativeTo) || IsTemporalDate(relativeTo)) return relativeTo; - if (IsTemporalDateTime(relativeTo)) return TemporalDateTimeToDate(relativeTo); + if (IsTemporalZonedDateTime(relativeTo)) return { zonedRelativeTo: relativeTo }; + if (IsTemporalDate(relativeTo)) return { plainRelativeTo: relativeTo }; + if (IsTemporalDateTime(relativeTo)) return { plainRelativeTo: TemporalDateTimeToDate(relativeTo) }; calendar = GetTemporalCalendarSlotValueWithISODefault(relativeTo); const fieldNames = CalendarFields(calendar, ['day', 'month', 'monthCode', 'year']); Call(ArrayPrototypePush, fieldNames, [ @@ -1040,7 +1046,7 @@ export function ToRelativeTemporalObject(options) { if (!IsBuiltinCalendar(calendar)) throw new RangeError(`invalid calendar identifier ${calendar}`); calendar = ASCIILowercase(calendar); } - if (timeZone === undefined) return CreateTemporalDate(year, month, day, calendar); + if (timeZone === undefined) return { plainRelativeTo: CreateTemporalDate(year, month, day, calendar) }; const offsetNs = offsetBehaviour === 'option' ? ParseDateTimeUTCOffset(offset) : 0; const epochNanoseconds = InterpretISODateTimeOffset( year, @@ -1059,7 +1065,7 @@ export function ToRelativeTemporalObject(options) { 'reject', matchMinutes ); - return CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar); + return { zonedRelativeTo: CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar) }; } export function DefaultTemporalLargestUnit( @@ -3574,16 +3580,12 @@ export function BalancePossiblyInfiniteTimeDurationRelative( return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; } -export function UnbalanceDateDurationRelative(years, months, weeks, days, largestUnit, relativeTo) { +export function UnbalanceDateDurationRelative(years, months, weeks, days, largestUnit, plainRelativeTo) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); const sign = DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0); if (sign === 0) return { years, months, weeks, days }; - let calendar; - if (relativeTo) { - relativeTo = ToTemporalDate(relativeTo); - calendar = GetSlot(relativeTo, CALENDAR); - } + const calendar = plainRelativeTo ? GetSlot(plainRelativeTo, CALENDAR) : undefined; const oneYear = new TemporalDuration(sign); const oneMonth = new TemporalDuration(0, sign); @@ -3601,6 +3603,7 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges break; case 'month': { + if (years.isZero()) break; if (!calendar) throw new RangeError('a starting point is required for months balancing'); // balance years down to months let dateAdd, dateUntil; @@ -3609,12 +3612,12 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges dateUntil = GetMethod(calendar, 'dateUntil'); } while (!years.isZero()) { - const newRelativeTo = CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd); + const newRelativeTo = CalendarDateAdd(calendar, plainRelativeTo, oneYear, undefined, dateAdd); const untilOptions = ObjectCreate(null); untilOptions.largestUnit = 'month'; - const untilResult = CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil); + const untilResult = CalendarDateUntil(calendar, plainRelativeTo, newRelativeTo, untilOptions, dateUntil); const oneYearMonths = GetSlot(untilResult, MONTHS); - relativeTo = newRelativeTo; + plainRelativeTo = newRelativeTo; months = months.add(oneYearMonths); years = years.subtract(sign); } @@ -3622,12 +3625,18 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges break; case 'week': { + if (years.isZero() && months.isZero()) break; if (!calendar) throw new RangeError('a starting point is required for weeks balancing'); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; // balance years down to days while (!years.isZero()) { let oneYearDays; - ({ relativeTo, days: oneYearDays } = MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneYearDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneYear, + dateAdd + )); days = days.add(oneYearDays); years = years.subtract(sign); } @@ -3635,7 +3644,12 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges // balance months down to days while (!months.isZero()) { let oneMonthDays; - ({ relativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); days = days.add(oneMonthDays); months = months.subtract(sign); } @@ -3649,7 +3663,12 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges // balance years down to days while (!years.isZero()) { let oneYearDays; - ({ relativeTo, days: oneYearDays } = MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneYearDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneYear, + dateAdd + )); days = days.add(oneYearDays); years = years.subtract(sign); } @@ -3657,7 +3676,12 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges // balance months down to days while (!months.isZero()) { let oneMonthDays; - ({ relativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); days = days.add(oneMonthDays); months = months.subtract(sign); } @@ -3665,7 +3689,12 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges // balance weeks down to days while (!weeks.isZero()) { let oneWeekDays; - ({ relativeTo, days: oneWeekDays } = MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneWeekDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneWeek, + dateAdd + )); days = days.add(oneWeekDays); weeks = weeks.subtract(sign); } @@ -3681,17 +3710,16 @@ export function UnbalanceDateDurationRelative(years, months, weeks, days, larges }; } -export function BalanceDateDurationRelative(years, months, weeks, days, largestUnit, relativeTo) { +export function BalanceDateDurationRelative(years, months, weeks, days, largestUnit, plainRelativeTo) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); const sign = DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0); - if (sign === 0) return { years, months, weeks, days }; - - let calendar; - if (relativeTo) { - relativeTo = ToTemporalDate(relativeTo); - calendar = GetSlot(relativeTo, CALENDAR); + if (sign === 0 || (largestUnit !== 'year' && largestUnit !== 'month' && largestUnit !== 'week')) { + return { years, months, weeks, days }; } + if (!plainRelativeTo) throw new RangeError(`a starting point is required for ${largestUnit}s balancing`); + const calendar = GetSlot(plainRelativeTo, CALENDAR); + const oneYear = new TemporalDuration(sign); const oneMonth = new TemporalDuration(0, sign); const oneWeek = new TemporalDuration(0, 0, sign); @@ -3704,72 +3732,109 @@ export function BalanceDateDurationRelative(years, months, weeks, days, largestU switch (largestUnit) { case 'year': { - if (!calendar) throw new RangeError('a starting point is required for years balancing'); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; // balance days up to years let newRelativeTo, oneYearDays; - ({ relativeTo: newRelativeTo, days: oneYearDays } = MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd)); + ({ relativeTo: newRelativeTo, days: oneYearDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneYear, + dateAdd + )); while (days.abs().geq(MathAbs(oneYearDays))) { days = days.subtract(oneYearDays); years = years.add(sign); - relativeTo = newRelativeTo; - ({ relativeTo: newRelativeTo, days: oneYearDays } = MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd)); + plainRelativeTo = newRelativeTo; + ({ relativeTo: newRelativeTo, days: oneYearDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneYear, + dateAdd + )); } // balance days up to months let oneMonthDays; - ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); while (days.abs().geq(MathAbs(oneMonthDays))) { days = days.subtract(oneMonthDays); months = months.add(sign); - relativeTo = newRelativeTo; - ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + plainRelativeTo = newRelativeTo; + ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); } // balance months up to years - newRelativeTo = CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd); + newRelativeTo = CalendarDateAdd(calendar, plainRelativeTo, oneYear, undefined, dateAdd); const dateUntil = typeof calendar !== 'string' ? GetMethod(calendar, 'dateUntil') : undefined; const untilOptions = ObjectCreate(null); untilOptions.largestUnit = 'month'; - let untilResult = CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil); + let untilResult = CalendarDateUntil(calendar, plainRelativeTo, newRelativeTo, untilOptions, dateUntil); let oneYearMonths = GetSlot(untilResult, MONTHS); while (months.abs().geq(MathAbs(oneYearMonths))) { months = months.subtract(oneYearMonths); years = years.add(sign); - relativeTo = newRelativeTo; - newRelativeTo = CalendarDateAdd(calendar, relativeTo, oneYear, undefined, dateAdd); + plainRelativeTo = newRelativeTo; + newRelativeTo = CalendarDateAdd(calendar, plainRelativeTo, oneYear, undefined, dateAdd); const untilOptions = ObjectCreate(null); untilOptions.largestUnit = 'month'; - untilResult = CalendarDateUntil(calendar, relativeTo, newRelativeTo, untilOptions, dateUntil); + untilResult = CalendarDateUntil(calendar, plainRelativeTo, newRelativeTo, untilOptions, dateUntil); oneYearMonths = GetSlot(untilResult, MONTHS); } break; } case 'month': { - if (!calendar) throw new RangeError('a starting point is required for months balancing'); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; // balance days up to months let newRelativeTo, oneMonthDays; - ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); while (days.abs().geq(MathAbs(oneMonthDays))) { days = days.subtract(oneMonthDays); months = months.add(sign); - relativeTo = newRelativeTo; - ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + plainRelativeTo = newRelativeTo; + ({ relativeTo: newRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); } break; } case 'week': { - if (!calendar) throw new RangeError('a starting point is required for weeks balancing'); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; // balance days up to weeks let newRelativeTo, oneWeekDays; - ({ relativeTo: newRelativeTo, days: oneWeekDays } = MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd)); + ({ relativeTo: newRelativeTo, days: oneWeekDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneWeek, + dateAdd + )); while (days.abs().geq(MathAbs(oneWeekDays))) { days = days.subtract(oneWeekDays); weeks = weeks.add(sign); - relativeTo = newRelativeTo; - ({ relativeTo: newRelativeTo, days: oneWeekDays } = MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd)); + plainRelativeTo = newRelativeTo; + ({ relativeTo: newRelativeTo, days: oneWeekDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneWeek, + dateAdd + )); } break; } @@ -4558,6 +4623,9 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, settings.largestUnit, resolvedOptions)); if (settings.smallestUnit !== 'nanosecond' || settings.roundingIncrement !== 1) { + const plainRelativeToWillBeUsed = + settings.smallestUnit === 'year' || settings.smallestUnit === 'month' || settings.smallestUnit === 'week'; + const plainRelativeTo = plainRelativeToWillBeUsed ? ToTemporalDate(zonedDateTime) : undefined; ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( years, months, @@ -4572,6 +4640,7 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, + plainRelativeTo, zonedDateTime )); ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = @@ -4673,14 +4742,15 @@ export function AddDuration( ms2, µs2, ns2, - relativeTo + plainRelativeTo, + zonedRelativeTo ) { const largestUnit1 = DefaultTemporalLargestUnit(y1, mon1, w1, d1, h1, min1, s1, ms1, µs1, ns1); const largestUnit2 = DefaultTemporalLargestUnit(y2, mon2, w2, d2, h2, min2, s2, ms2, µs2, ns2); const largestUnit = LargerOfTwoTemporalUnits(largestUnit1, largestUnit2); let years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds; - if (!relativeTo) { + if (!zonedRelativeTo && !plainRelativeTo) { if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week') { throw new RangeError('relativeTo is required for years, months, or weeks arithmetic'); } @@ -4695,20 +4765,20 @@ export function AddDuration( bigInt(ns1).add(ns2), largestUnit )); - } else if (IsTemporalDate(relativeTo)) { + } else if (plainRelativeTo) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); - const calendar = GetSlot(relativeTo, CALENDAR); + const calendar = GetSlot(plainRelativeTo, CALENDAR); const dateDuration1 = new TemporalDuration(y1, mon1, w1, d1, 0, 0, 0, 0, 0, 0); const dateDuration2 = new TemporalDuration(y2, mon2, w2, d2, 0, 0, 0, 0, 0, 0); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; - const intermediate = CalendarDateAdd(calendar, relativeTo, dateDuration1, undefined, dateAdd); + const intermediate = CalendarDateAdd(calendar, plainRelativeTo, dateDuration1, undefined, dateAdd); const end = CalendarDateAdd(calendar, intermediate, dateDuration2, undefined, dateAdd); const dateLargestUnit = LargerOfTwoTemporalUnits('day', largestUnit); const differenceOptions = ObjectCreate(null); differenceOptions.largestUnit = dateLargestUnit; - const untilResult = CalendarDateUntil(calendar, relativeTo, end, differenceOptions); + const untilResult = CalendarDateUntil(calendar, plainRelativeTo, end, differenceOptions); years = GetSlot(untilResult, YEARS); months = GetSlot(untilResult, MONTHS); weeks = GetSlot(untilResult, WEEKS); @@ -4725,12 +4795,12 @@ export function AddDuration( largestUnit )); } else { - // relativeTo is a ZonedDateTime + // zonedRelativeTo is defined const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); - const timeZone = GetSlot(relativeTo, TIME_ZONE); - const calendar = GetSlot(relativeTo, CALENDAR); + const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE); + const calendar = GetSlot(zonedRelativeTo, CALENDAR); const intermediateNs = AddZonedDateTime( - GetSlot(relativeTo, INSTANT), + GetSlot(zonedRelativeTo, INSTANT), timeZone, calendar, y1, @@ -4766,7 +4836,7 @@ export function AddDuration( weeks = 0; days = 0; ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceInstant( - GetSlot(relativeTo, EPOCHNANOSECONDS), + GetSlot(zonedRelativeTo, EPOCHNANOSECONDS), endNs, 1, 'nanosecond', @@ -4776,7 +4846,7 @@ export function AddDuration( } else { ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceZonedDateTime( - GetSlot(relativeTo, EPOCHNANOSECONDS), + GetSlot(zonedRelativeTo, EPOCHNANOSECONDS), endNs, timeZone, calendar, @@ -4923,7 +4993,7 @@ export function AddDurationToOrSubtractDurationFromDuration(operation, duration, let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ToTemporalDurationRecord(other); options = GetOptionsObject(options); - const relativeTo = ToRelativeTemporalObject(options); + const { plainRelativeTo, zonedRelativeTo } = ToRelativeTemporalObject(options); ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = AddDuration( GetSlot(duration, YEARS), GetSlot(duration, MONTHS), @@ -4945,7 +5015,8 @@ export function AddDurationToOrSubtractDurationFromDuration(operation, duration, sign * milliseconds, sign * microseconds, sign * nanoseconds, - relativeTo + plainRelativeTo, + zonedRelativeTo )); const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); @@ -5294,10 +5365,9 @@ export function AdjustRoundedDurationDays( increment, unit, roundingMode, - relativeTo + zonedRelativeTo ) { if ( - !IsTemporalZonedDateTime(relativeTo) || unit === 'year' || unit === 'month' || unit === 'week' || @@ -5327,10 +5397,10 @@ export function AdjustRoundedDurationDays( ); const direction = MathSign(timeRemainderNs.toJSNumber()); - const timeZone = GetSlot(relativeTo, TIME_ZONE); - const calendar = GetSlot(relativeTo, CALENDAR); + const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE); + const calendar = GetSlot(zonedRelativeTo, CALENDAR); const dayStart = AddZonedDateTime( - GetSlot(relativeTo, INSTANT), + GetSlot(zonedRelativeTo, INSTANT), timeZone, calendar, years, @@ -5385,7 +5455,8 @@ export function AdjustRoundedDurationDays( 0, 0, 0, - relativeTo + /* plainRelativeTo = */ undefined, + zonedRelativeTo )); ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( years, @@ -5430,11 +5501,12 @@ export function RoundDuration( increment, unit, roundingMode, - relativeTo = undefined + plainRelativeTo = undefined, + zonedRelativeTo = undefined ) { const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); - if ((unit === 'year' || unit === 'month' || unit === 'week') && !relativeTo) { + if ((unit === 'year' || unit === 'month' || unit === 'week') && !plainRelativeTo) { throw new RangeError(`A starting point is required for ${unit}s rounding`); } @@ -5444,8 +5516,8 @@ export function RoundDuration( if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') { nanoseconds = TotalDurationNanoseconds(0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); let deltaDays; - if (IsTemporalZonedDateTime(relativeTo)) { - const intermediate = MoveRelativeZonedDateTime(relativeTo, years, months, weeks, days); + if (zonedRelativeTo) { + const intermediate = MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days); ({ days: deltaDays, nanoseconds, dayLengthNs } = NanosecondsToDays(nanoseconds, intermediate)); } else { ({ quotient: deltaDays, remainder: nanoseconds } = nanoseconds.divmod(DAY_NANOS)); @@ -5459,32 +5531,36 @@ export function RoundDuration( let total; switch (unit) { case 'year': { - relativeTo = ToTemporalDate(relativeTo); - const calendar = GetSlot(relativeTo, CALENDAR); + const calendar = GetSlot(plainRelativeTo, CALENDAR); // convert months and weeks to days by calculating difference( // relativeTo + years, relativeTo + { years, months, weeks }) const yearsDuration = new TemporalDuration(years); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; - const yearsLater = CalendarDateAdd(calendar, relativeTo, yearsDuration, undefined, dateAdd); + const yearsLater = CalendarDateAdd(calendar, plainRelativeTo, yearsDuration, undefined, dateAdd); const yearsMonthsWeeks = new TemporalDuration(years, months, weeks); - const yearsMonthsWeeksLater = CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd); + const yearsMonthsWeeksLater = CalendarDateAdd(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd); const monthsWeeksInDays = DaysUntil(yearsLater, yearsMonthsWeeksLater); - relativeTo = yearsLater; + plainRelativeTo = yearsLater; days += monthsWeeksInDays; const wholeDays = new TemporalDuration(0, 0, 0, days); - const wholeDaysLater = CalendarDateAdd(calendar, relativeTo, wholeDays, undefined, dateAdd); + const wholeDaysLater = CalendarDateAdd(calendar, plainRelativeTo, wholeDays, undefined, dateAdd); const untilOptions = ObjectCreate(null); untilOptions.largestUnit = 'year'; - const yearsPassed = GetSlot(CalendarDateUntil(calendar, relativeTo, wholeDaysLater, untilOptions), YEARS); + const yearsPassed = GetSlot(CalendarDateUntil(calendar, plainRelativeTo, wholeDaysLater, untilOptions), YEARS); years += yearsPassed; const yearsPassedDuration = new TemporalDuration(yearsPassed); let daysPassed; - ({ relativeTo, days: daysPassed } = MoveRelativeDate(calendar, relativeTo, yearsPassedDuration, dateAdd)); + ({ relativeTo: plainRelativeTo, days: daysPassed } = MoveRelativeDate( + calendar, + plainRelativeTo, + yearsPassedDuration, + dateAdd + )); days -= daysPassed; const oneYear = new TemporalDuration(days < 0 ? -1 : 1); - let { days: oneYearDays } = MoveRelativeDate(calendar, relativeTo, oneYear, dateAdd); + let { days: oneYearDays } = MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd); // Note that `nanoseconds` below (here and in similar code for months, // weeks, and days further below) isn't actually nanoseconds for the @@ -5503,18 +5579,17 @@ export function RoundDuration( break; } case 'month': { - relativeTo = ToTemporalDate(relativeTo); - const calendar = GetSlot(relativeTo, CALENDAR); + const calendar = GetSlot(plainRelativeTo, CALENDAR); // convert weeks to days by calculating difference(relativeTo + // { years, months }, relativeTo + { years, months, weeks }) const yearsMonths = new TemporalDuration(years, months); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; - const yearsMonthsLater = CalendarDateAdd(calendar, relativeTo, yearsMonths, undefined, dateAdd); + const yearsMonthsLater = CalendarDateAdd(calendar, plainRelativeTo, yearsMonths, undefined, dateAdd); const yearsMonthsWeeks = new TemporalDuration(years, months, weeks); - const yearsMonthsWeeksLater = CalendarDateAdd(calendar, relativeTo, yearsMonthsWeeks, undefined, dateAdd); + const yearsMonthsWeeksLater = CalendarDateAdd(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd); const weeksInDays = DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater); - relativeTo = yearsMonthsLater; + plainRelativeTo = yearsMonthsLater; days += weeksInDays; // Months may be different lengths of days depending on the calendar, @@ -5522,11 +5597,21 @@ export function RoundDuration( const sign = MathSign(days); const oneMonth = new TemporalDuration(0, days < 0 ? -1 : 1); let oneMonthDays; - ({ relativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); while (MathAbs(days) >= MathAbs(oneMonthDays)) { months += sign; days -= oneMonthDays; - ({ relativeTo, days: oneMonthDays } = MoveRelativeDate(calendar, relativeTo, oneMonth, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneMonthDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneMonth, + dateAdd + )); } oneMonthDays = MathAbs(oneMonthDays); const divisor = bigInt(oneMonthDays).multiply(dayLengthNs); @@ -5539,8 +5624,7 @@ export function RoundDuration( break; } case 'week': { - relativeTo = ToTemporalDate(relativeTo); - const calendar = GetSlot(relativeTo, CALENDAR); + const calendar = GetSlot(plainRelativeTo, CALENDAR); // Weeks may be different lengths of days depending on the calendar, // convert days to weeks in a loop as described above under 'years'. @@ -5548,11 +5632,21 @@ export function RoundDuration( const oneWeek = new TemporalDuration(0, 0, days < 0 ? -1 : 1); const dateAdd = typeof calendar !== 'string' ? GetMethod(calendar, 'dateAdd') : undefined; let oneWeekDays; - ({ relativeTo, days: oneWeekDays } = MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneWeekDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneWeek, + dateAdd + )); while (MathAbs(days) >= MathAbs(oneWeekDays)) { weeks += sign; days -= oneWeekDays; - ({ relativeTo, days: oneWeekDays } = MoveRelativeDate(calendar, relativeTo, oneWeek, dateAdd)); + ({ relativeTo: plainRelativeTo, days: oneWeekDays } = MoveRelativeDate( + calendar, + plainRelativeTo, + oneWeek, + dateAdd + )); } oneWeekDays = MathAbs(oneWeekDays); const divisor = bigInt(oneWeekDays).multiply(dayLengthNs); diff --git a/polyfill/test/ecmascript.mjs b/polyfill/test/ecmascript.mjs index 94d3c752b3..8c7072156b 100644 --- a/polyfill/test/ecmascript.mjs +++ b/polyfill/test/ecmascript.mjs @@ -120,6 +120,96 @@ describe('ECMAScript', () => { } }); }); + + describe('ToRelativeTemporalObject', () => { + it('bare date-time string', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ relativeTo: '2019-11-01T00:00' }); + equal(`${plainRelativeTo}`, '2019-11-01'); + equal(zonedRelativeTo, undefined); + }); + + it('bare date-time property bag', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: { year: 2019, month: 11, day: 1 } + }); + equal(`${plainRelativeTo}`, '2019-11-01'); + equal(zonedRelativeTo, undefined); + }); + + it('date-time + offset string', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: '2019-11-01T00:00-07:00' + }); + equal(`${plainRelativeTo}`, '2019-11-01'); + equal(zonedRelativeTo, undefined); + }); + + it('date-time + offset property bag', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: { year: 2019, month: 11, day: 1, offset: '-07:00' } + }); + equal(`${plainRelativeTo}`, '2019-11-01'); + equal(zonedRelativeTo, undefined); + }); + + it('date-time + annotation string', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: '2019-11-01T00:00[-07:00]' + }); + equal(plainRelativeTo, undefined); + equal(`${zonedRelativeTo}`, '2019-11-01T00:00:00-07:00[-07:00]'); + }); + + it('date-time + annotation property bag', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: { year: 2019, month: 11, day: 1, timeZone: '-07:00' } + }); + equal(plainRelativeTo, undefined); + equal(`${zonedRelativeTo}`, '2019-11-01T00:00:00-07:00[-07:00]'); + }); + + it('date-time + offset + annotation string', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: '2019-11-01T00:00+00:00[UTC]' + }); + equal(plainRelativeTo, undefined); + equal(`${zonedRelativeTo}`, '2019-11-01T00:00:00+00:00[UTC]'); + }); + + it('date-time + offset + annotation property bag', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: { year: 2019, month: 11, day: 1, offset: '+00:00', timeZone: 'UTC' } + }); + equal(plainRelativeTo, undefined); + equal(`${zonedRelativeTo}`, '2019-11-01T00:00:00+00:00[UTC]'); + }); + + it('date-time + Z + offset', () => { + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject({ + relativeTo: '2019-11-01T00:00Z[-07:00]' + }); + equal(plainRelativeTo, undefined); + equal(`${zonedRelativeTo}`, '2019-10-31T17:00:00-07:00[-07:00]'); + }); + + it('date-time + Z', () => { + throws(() => ES.ToRelativeTemporalObject({ relativeTo: '2019-11-01T00:00Z' }), RangeError); + }); + + it('string offset does not agree', () => { + throws(() => ES.ToRelativeTemporalObject({ relativeTo: '2019-11-01T00:00+04:15[UTC]' }), RangeError); + }); + + it('property bag offset does not agree', () => { + throws( + () => + ES.ToRelativeTemporalObject({ + relativeTo: { year: 2019, month: 11, day: 1, offset: '+04:15', timeZone: 'UTC' } + }), + RangeError + ); + }); + }); }); import { normalize } from 'path'; diff --git a/spec/abstractops.html b/spec/abstractops.html index 7f1a9d5a3d..8718d3637a 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -570,27 +570,30 @@

ToRelativeTemporalObject ( _options_: an Object - ): either a normal completion containing either a Temporal.ZonedDateTime object or a Temporal.PlainDate object, or a throw completion

+ ): either a normal completion containing a Record with fields [[PlainRelativeTo]] and [[ZonedRelativeTo]], or a throw completion

description
It examines the value of the `relativeTo` property of its _options_ argument. - If the value is *undefined*, it returns *undefined*. + If the value is *undefined*, both the [[PlainRelativeTo]] and [[ZonedRelativeTo]] fields of the returned Record are *undefined*. If the value is not a String or an Object, it throws a *TypeError*. - Otherwise, it attempts to return a Temporal.ZonedDateTime instance or Temporal.PlainDate instance, in order of preference, by converting the value. + Otherwise, it attempts to return a Temporal.ZonedDateTime instance in the [[ZonedRelativeTo]] field, or a Temporal.PlainDate instance in the [[PlainRelativeTo]] field, in order of preference, by converting the value. If neither of those are possible, it throws a *RangeError*.
1. Let _value_ be ? Get(_options_, *"relativeTo"*). - 1. If _value_ is *undefined*, return *undefined*. + 1. If _value_ is *undefined*, return the Record { [[ZonedRelativeTo]]: *undefined*, [[PlainRelativeTo]]: *undefined* }. 1. Let _offsetBehaviour_ be ~option~. 1. Let _matchBehaviour_ be ~match exactly~. 1. If Type(_value_) is Object, then - 1. If _value_ has either an [[InitializedTemporalDate]] or [[InitializedTemporalZonedDateTime]] internal slot, then - 1. Return _value_. + 1. If _value_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + 1. Return the Record { [[PlainRelativeTo]]: *undefined*, [[ZonedRelativeTo]]: _value_ }. + 1. If _value_ has an [[InitializedTemporalDate]] internal slot, then + 1. Return the Record { [[PlainRelativeTo]]: _value_, [[ZonedRelativeTo]]: *undefined* }. 1. If _value_ has an [[InitializedTemporalDateTime]] internal slot, then - 1. Return ! CreateTemporalDate(_value_.[[ISOYear]], _value_.[[ISOMonth]], _value_.[[ISODay]], _value_.[[Calendar]]). + 1. Let _plainDate_ be ! CreateTemporalDate(_value_.[[ISOYear]], _value_.[[ISOMonth]], _value_.[[ISODay]], _value_.[[Calendar]]). + 1. Return the Record { [[PlainRelativeTo]]: _plainDate_, [[ZonedRelativeTo]]: *undefined* }. 1. Let _calendar_ be ? GetTemporalCalendarSlotValueWithISODefault(_value_). 1. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). 1. Append *"hour"*, *"microsecond"*, *"millisecond"*, *"minute"*, *"nanosecond"*, *"offset"*, *"second"*, and *"timeZone"* to _fieldNames_. @@ -623,13 +626,15 @@

ToRelativeTemporalObject ( 1. If IsBuiltinCalendar(_calendar_) is *false*, throw a *RangeError* exception. 1. Set _calendar_ to the ASCII-lowercase of _calendar_. 1. If _timeZone_ is *undefined*, then - 1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendar_). + 1. Let _plainDate_ be ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendar_). + 1. Return the Record { [[PlainRelativeTo]]: _plainDate, [[ZonedRelativeTo]]: *undefined* }. 1. If _offsetBehaviour_ is ~option~, then 1. Let _offsetNs_ be ? ParseDateTimeUTCOffset(_offsetString_). 1. Else, 1. Let _offsetNs_ be 0. 1. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _offsetBehaviour_, _offsetNs_, _timeZone_, *"compatible"*, *"reject"*, _matchBehaviour_). - 1. Return ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZone_, _calendar_). + 1. Let _zonedRelativeTo_ be ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZone_, _calendar_). + 1. Return the Record { [[PlainRelativeTo]]: *undefined*, [[ZonedRelativeTo]]: _zonedRelativeTo_ }. diff --git a/spec/duration.html b/spec/duration.html index e1983e3885..8e1b9bea71 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -94,13 +94,15 @@

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

1. Set _one_ to ? ToTemporalDuration(_one_). 1. Set _two_ to ? ToTemporalDuration(_two_). 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). - 1. Let _shift1_ be ? CalculateOffsetShift(_relativeTo_, _one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]]). - 1. Let _shift2_ be ? CalculateOffsetShift(_relativeTo_, _two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]]). + 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 _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _unbalanceResult1_ be ? UnbalanceDateDurationRelative(_one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]], *"day"*, _relativeTo_). - 1. Let _unbalanceResult2_ be ? UnbalanceDateDurationRelative(_two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], *"day"*, _relativeTo_). + 1. If _zonedRelativeTo_ is not *undefined*, set _plainRelativeTo_ to ? ToTemporalDate(_zonedRelativeTo_). + 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]]. 1. Let _days2_ be _unbalanceResult2_.[[Days]]. 1. Else, @@ -415,7 +417,9 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. Let _largestUnitPresent_ be *true*. 1. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads *"relativeTo"*, ToTemporalRoundingIncrement reads *"roundingIncrement"* and ToTemporalRoundingMode reads *"roundingMode"*). 1. Let _largestUnit_ be ? GetTemporalUnit(_roundTo_, *"largestUnit"*, ~datetime~, *undefined*, « *"auto"* »). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_roundTo_). + 1. Let _relativeToRecord_ be ? ToRelativeTemporalObject(_roundTo_). + 1. Let _zonedRelativeTo_ be _relativeToRecord_.[[ZonedRelativeTo]]. + 1. Let _plainRelativeTo_ be _relativeToRecord_.[[PlainRelativeTo]]. 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_roundTo_). 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). 1. Let _smallestUnit_ be ? GetTemporalUnit(_roundTo_, *"smallestUnit"*, ~datetime~, *undefined*). @@ -435,20 +439,23 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. Let _maximum_ be ! MaximumTemporalDurationRoundingIncrement(_smallestUnit_). 1. If _maximum_ is not *undefined*, perform ? ValidateTemporalRoundingIncrement(_roundingIncrement_, _maximum_, *false*). 1. Let _hoursToDaysConversionMayOccur_ be *false*. - 1. If _duration_.[[Days]] ≠ 0 and _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, set _hoursToDaysConversionMayOccur_ to *true*. + 1. If _duration_.[[Days]] ≠ 0 and _zonedRelativeTo_ is not *undefined*, set _hoursToDaysConversionMayOccur_ to *true*. 1. Else if _duration_.[[Hours]] ≥ 24, set _hoursToDaysConversionMayOccur_ to *true*. 1. If _smallestUnit_ is *"nanosecond"*, and _roundingIncrement_ = 1, and _largestUnit_ is _existingLargestUnit_, and _duration_.[[Years]] = 0, and _duration_.[[Months]] = 0, and _duration_.[[Weeks]] = 0, and _hoursToDaysConversionMayOccur_ is *false*, and _duration_.[[Minutes]] < 60, and _duration_.[[Seconds]] < 60, and _duration_.[[Milliseconds]] < 1000, and _duration_.[[Microseconds]] < 1000, and _duration_.[[Nanoseconds]] < 1000, then 1. NOTE: The above conditions mean that the operation will have no effect: the smallest unit and rounding increment will leave the total duration unchanged, and it can be determined without calling a calendar or time zone method that no balancing will take place. 1. Return ! CreateTemporalDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). - 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_). - 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 1. If _zonedRelativeTo_ is not *undefined*; and _smallestUnit_ is any of *"year"*, *"month"*, *"week"*; and any of _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]] ≠ 0; then + 1. NOTE: The above conditions mean that the corresponding `Temporal.PlainDate` for _zonedRelativeTo_ will be used in one of the operations below. + 1. Set _plainRelativeTo_ to ? ToTemporalDate(_zonedRelativeTo_). + 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _plainRelativeTo_). + 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _plainRelativeTo_, _zonedRelativeTo_). 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. - 1. If _relativeTo_ is not *undefined* and _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then - 1. Set _roundResult_ to ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). - 1. Let _balanceResult_ be ? BalanceTimeDurationRelative(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_). + 1. If _zonedRelativeTo_ is not *undefined*, then + 1. Set _roundResult_ to ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedRelativeTo_). + 1. Let _balanceResult_ be ? BalanceTimeDurationRelative(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_, _zonedRelativeTo_). 1. Else, 1. Let _balanceResult_ be ? BalanceTimeDuration(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_). - 1. Let _result_ be ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _balanceResult_.[[Days]], _largestUnit_, _relativeTo_). + 1. Let _result_ be ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _balanceResult_.[[Days]], _largestUnit_, _plainRelativeTo_). 1. Return ! CreateTemporalDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]]).
@@ -469,18 +476,23 @@

Temporal.Duration.prototype.total ( _totalOf_ )

1. Else, 1. Set _totalOf_ to ? GetOptionsObject(_totalOf_). 1. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads *"relativeTo"*). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_totalOf_). + 1. Let _relativeToRecord_ be ? ToRelativeTemporalObject(_totalOf_). + 1. Let _zonedRelativeTo_ be _relativeToRecord_.[[ZonedRelativeTo]]. + 1. Let _plainRelativeTo_ be _relativeToRecord_.[[PlainRelativeTo]]. 1. Let _unit_ be ? GetTemporalUnit(_totalOf_, *"unit"*, ~datetime~, ~required~). - 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_). - 1. If Type(_relativeTo_) is Object and _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then - 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_relativeTo_, _unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], 0). + 1. If _zonedRelativeTo_ is not *undefined*; and _unit_ is any of *"year"*, *"month"*, *"week"*; and any of _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]] ≠ 0; then + 1. NOTE: The above conditions mean that the corresponding `Temporal.PlainDate` for _zonedRelativeTo_ will be used in one of the operations below. + 1. Set _plainRelativeTo_ to ? ToTemporalDate(_zonedRelativeTo_). + 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _plainRelativeTo_). + 1. If _zonedRelativeTo_ is not *undefined*, then + 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], 0). 1. Let _balanceResult_ be ? BalancePossiblyInfiniteTimeDurationRelative(_unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _unit_, _intermediate_). 1. Else, 1. Let _balanceResult_ be BalancePossiblyInfiniteTimeDuration(_unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _unit_). 1. If _balanceResult_ is ~positive overflow~, return *+∞*𝔽. 1. If _balanceResult_ is ~negative overflow~, return *-∞*𝔽. 1. Assert: _balanceResult_ is a Time Duration Record. - 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_). + 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _plainRelativeTo_, _zonedRelativeTo_). 1. Return 𝔽(_roundRecord_.[[Total]]). @@ -1357,7 +1369,7 @@

_weeks_: an integer, _days_: an integer, _largestUnit_: a String, - _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, + _plainRelativeTo_: *undefined* or a Temporal.PlainDate, )

@@ -1374,12 +1386,12 @@

1. Let _oneYear_ be ! CreateTemporalDuration(_sign_, 0, 0, 0, 0, 0, 0, 0, 0, 0). 1. Let _oneMonth_ be ! CreateTemporalDuration(0, _sign_, 0, 0, 0, 0, 0, 0, 0, 0). 1. Let _oneWeek_ be ! CreateTemporalDuration(0, 0, _sign_, 0, 0, 0, 0, 0, 0, 0). - 1. If _relativeTo_ is not *undefined*, then - 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. If _plainRelativeTo_ is not *undefined*, then + 1. Let _calendar_ be _plainRelativeTo_.[[Calendar]]. 1. Else, 1. Let _calendar_ be *undefined*. 1. If _largestUnit_ is *"month"*, then + 1. If _years_ = 0, return ? CreateDateDurationRecord(0, _months_, _weeks_, _days_). 1. If _calendar_ is *undefined*, then 1. Throw a *RangeError* exception. 1. If _calendar_ is an Object, then @@ -1389,15 +1401,16 @@

1. Let _dateAdd_ be ~unused~. 1. Let _dateUntil_ be ~unused~. 1. Repeat, while _years_ ≠ 0, - 1. Let _newRelativeTo_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _oneYear_, *undefined*, _dateAdd_). + 1. Let _newRelativeTo_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _oneYear_, *undefined*, _dateAdd_). 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). - 1. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + 1. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _plainRelativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). 1. Let _oneYearMonths_ be _untilResult_.[[Months]]. - 1. Set _relativeTo_ to _newRelativeTo_. + 1. Set _plainRelativeTo_ to _newRelativeTo_. 1. Set _years_ to _years_ - _sign_. 1. Set _months_ to _months_ + _oneYearMonths_. 1. Else if _largestUnit_ is *"week"*, then + 1. If _years_ = 0 and _months_ = 0, return ? CreateDateDurationRecord(0, 0, _weeks_, _days_). 1. If _calendar_ is *undefined*, then 1. Throw a *RangeError* exception. 1. If _calendar_ is an Object, then @@ -1405,13 +1418,13 @@

1. Else, 1. Let _dateAdd_ be ~unused~. 1. Repeat, while _years_ ≠ 0, - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneYear_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _days_ to _days_ + _moveResult_.[[Days]]. 1. Set _years_ to _years_ - _sign_. 1. Repeat, while _months_ ≠ 0, - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _days_ to _days_ + _moveResult_.[[Days]]. 1. Set _months_ to _months_ - _sign_. 1. Else, @@ -1423,18 +1436,18 @@

1. Else, 1. Let _dateAdd_ be ~unused~. 1. Repeat, while _years_ ≠ 0, - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneYear_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _days_ to _days_ + _moveResult_.[[Days]]. 1. Set _years_ to _years_ - _sign_. 1. Repeat, while _months_ ≠ 0, - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _days_ to _days_ +_moveResult_.[[Days]]. 1. Set _months_ to _months_ - _sign_. 1. Repeat, while _weeks_ ≠ 0, - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneWeek_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneWeek_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _days_ to _days_ + _moveResult_.[[Days]]. 1. Set _weeks_ to _weeks_ - _sign_. 1. Return ? CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). @@ -1449,7 +1462,7 @@

_weeks_: an integer, _days_: an integer, _largestUnit_: a String, - _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, + _plainRelativeTo_: *undefined* or a Temporal.PlainDate, )

@@ -1461,71 +1474,70 @@

1. If _years_ = 0, and _months_ = 0, and _weeks_ = 0, and _days_ = 0, set _allZero_ to *true*. 1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _allZero_ is *true*, then 1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). - 1. If _relativeTo_ is *undefined*, then + 1. If _plainRelativeTo_ is *undefined*, then 1. Throw a *RangeError* exception. 1. Let _sign_ be ! DurationSign(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). 1. Assert: _sign_ ≠ 0. 1. Let _oneYear_ be ! CreateTemporalDuration(_sign_, 0, 0, 0, 0, 0, 0, 0, 0, 0). 1. Let _oneMonth_ be ! CreateTemporalDuration(0, _sign_, 0, 0, 0, 0, 0, 0, 0, 0). 1. Let _oneWeek_ be ! CreateTemporalDuration(0, 0, _sign_, 0, 0, 0, 0, 0, 0, 0). - 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. Let _calendar_ be _plainRelativeTo_.[[Calendar]]. 1. If _largestUnit_ is *"year"*, then 1. If _calendar_ is an Object, then 1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_, _dateAdd_). + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneYear_, _dateAdd_). 1. Let _newRelativeTo_ be _moveResult_.[[RelativeTo]]. 1. Let _oneYearDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_days_) ≥ abs(_oneYearDays_), 1. Set _days_ to _days_ - _oneYearDays_. 1. Set _years_ to _years_ + _sign_. - 1. Set _relativeTo_ to _newRelativeTo_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_, _dateAdd_). + 1. Set _plainRelativeTo_ to _newRelativeTo_. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneYear_, _dateAdd_). 1. Set _newRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneYearDays_ to _moveResult_.[[Days]]. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). 1. Set _newRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _oneMonthDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_days_) ≥ abs(_oneMonthDays_), 1. Set _days_ to _days_ - _oneMonthDays_. 1. Set _months_ to _months_ + _sign_. - 1. Set _relativeTo_ to _newRelativeTo_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _newRelativeTo_. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). 1. Set _newRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneMonthDays_ to _moveResult_.[[Days]]. - 1. Set _newRelativeTo_ to ? CalendarDateAdd(_calendar_, _relativeTo_, _oneYear_, *undefined*, _dateAdd_). + 1. Set _newRelativeTo_ to ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _oneYear_, *undefined*, _dateAdd_). 1. If _calendar_ is an Object, then 1. Let _dateUntil_ be ? GetMethod(_calendar_, *"dateUntil"*). 1. Else, 1. Let _dateUntil_ be ~unused~. 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). - 1. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + 1. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _plainRelativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). 1. Let _oneYearMonths_ be _untilResult_.[[Months]]. 1. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_), 1. Set _months_ to _months_ - _oneYearMonths_. 1. Set _years_ to _years_ + _sign_. - 1. Set _relativeTo_ to _newRelativeTo_. - 1. Set _newRelativeTo_ to ? CalendarDateAdd(_calendar_, _relativeTo_, _oneYear_, *undefined*, _dateAdd_). + 1. Set _plainRelativeTo_ to _newRelativeTo_. + 1. Set _newRelativeTo_ to ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _oneYear_, *undefined*, _dateAdd_). 1. Set _untilOptions_ to OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). - 1. Set _untilResult_ to ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + 1. Set _untilResult_ to ? CalendarDateUntil(_calendar_, _plainRelativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). 1. Set _oneYearMonths_ to _untilResult_.[[Months]]. 1. Else if _largestUnit_ is *"month"*, then 1. If _calendar_ is an Object, then 1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). 1. Let _newRelativeTo_ be _moveResult_.[[RelativeTo]]. 1. Let _oneMonthDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_days_) ≥ abs(_oneMonthDays_), 1. Set _days_ to _days_ - _oneMonthDays_. 1. Set _months_ to _months_ + _sign_. - 1. Set _relativeTo_ to _newRelativeTo_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _newRelativeTo_. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). 1. Set _newRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneMonthDays_ to _moveResult_.[[Days]]. 1. Else, @@ -1534,14 +1546,14 @@

1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneWeek_, _dateAdd_). + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneWeek_, _dateAdd_). 1. Let _newRelativeTo_ be _moveResult_.[[RelativeTo]]. 1. Let _oneWeekDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_days_) ≥ abs(_oneWeekDays_), 1. Set _days_ to _days_ - _oneWeekDays_. 1. Set _weeks_ to _weeks_ + _sign_. - 1. Set _relativeTo_ to _newRelativeTo_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneWeek_, _dateAdd_). + 1. Set _plainRelativeTo_ to _newRelativeTo_. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneWeek_, _dateAdd_). 1. Set _newRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneWeekDays_ to _moveResult_.[[Days]]. 1. Return ! CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). @@ -1571,47 +1583,49 @@

_ms2_: an integer, _mus2_: an integer, _ns2_: an integer, - _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, + _plainRelativeTo_: a Temporal.PlainDate or *undefined*, + _zonedRelativeTo_: a Temporal.ZonedDateTime or *undefined*, ): either a normal completion containing a Duration Record, or a throw completion

description
-
It adds the components of a second duration represented by _y2_ through _ns2_ to those of a first duration represented by _y1_ through _ns1_, and balances the duration relative to the given date _relativeTo_, to ensure that no mixed signs remain in the result.
+
It adds the components of a second duration represented by _y2_ through _ns2_ to those of a first duration represented by _y1_ through _ns1_, and balances the duration relative to the given date _zonedRelativeTo_ or _plainRelativeTo_, to ensure that no mixed signs remain in the result.
1. Let _largestUnit1_ be ! DefaultTemporalLargestUnit(_y1_, _mon1_, _w1_, _d1_, _h1_, _min1_, _s1_, _ms1_, _mus1_). 1. Let _largestUnit2_ be ! DefaultTemporalLargestUnit(_y2_, _mon2_, _w2_, _d2_, _h2_, _min2_, _s2_, _ms2_, _mus2_). 1. Let _largestUnit_ be ! LargerOfTwoTemporalUnits(_largestUnit1_, _largestUnit2_). - 1. If _relativeTo_ is *undefined*, then + 1. If _zonedRelativeTo_ is *undefined* and _plainRelativeTo_ is *undefined*, then 1. If _largestUnit_ is one of *"year"*, *"month"*, or *"week"*, then 1. Throw a *RangeError* exception. 1. Let _result_ be ? BalanceTimeDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). 1. Return ! CreateDurationRecord(0, 0, 0, _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). - 1. If _relativeTo_ has an [[InitializedTemporalDate]] internal slot, then - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. If _plainRelativeTo_ is not *undefined*, then + 1. Assert: _zonedRelativeTo_ is *undefined*. + 1. Let _calendar_ be _plainRelativeTo_.[[Calendar]]. 1. Let _dateDuration1_ be ! CreateTemporalDuration(_y1_, _mon1_, _w1_, _d1_, 0, 0, 0, 0, 0, 0). 1. Let _dateDuration2_ be ! CreateTemporalDuration(_y2_, _mon2_, _w2_, _d2_, 0, 0, 0, 0, 0, 0). 1. If _calendar_ is an Object, then 1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _intermediate_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _dateDuration1_, *undefined*, _dateAdd_). + 1. Let _intermediate_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _dateDuration1_, *undefined*, _dateAdd_). 1. Let _end_ be ? CalendarDateAdd(_calendar_, _intermediate_, _dateDuration2_, *undefined*, _dateAdd_). 1. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). 1. Let _differenceOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). - 1. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _end_, _differenceOptions_). + 1. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _plainRelativeTo_, _end_, _differenceOptions_). 1. Let _result_ be ? BalanceTimeDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). 1. Return ! CreateDurationRecord(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). - 1. Assert: _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot. - 1. Let _timeZone_ be _relativeTo_.[[TimeZone]]. - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. - 1. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, _mon1_, _w1_, _d1_, _h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_). + 1. Assert: _zonedRelativeTo_ is not *undefined*. + 1. Let _timeZone_ be _zonedRelativeTo_.[[TimeZone]]. + 1. Let _calendar_ be _zonedRelativeTo_.[[Calendar]]. + 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, _mon1_, _w1_, _d1_, _h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_). 1. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, _mon2_, _w2_, _d2_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). 1. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then - 1. Let _result_ be DifferenceInstant(_relativeTo_.[[Nanoseconds]], _endNs_, 1, *"nanosecond"*, _largestUnit_, *"halfExpand"*). + 1. Let _result_ be DifferenceInstant(_zonedRelativeTo_.[[Nanoseconds]], _endNs_, 1, *"nanosecond"*, _largestUnit_, *"halfExpand"*). 1. Return ! CreateDurationRecord(0, 0, 0, 0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). - 1. Return ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_, OrdinaryObjectCreate(*null*)). + 1. Return ? DifferenceZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_, OrdinaryObjectCreate(*null*)). @@ -1696,7 +1710,8 @@

_increment_: an integer, _unit_: a String, _roundingMode_: a String, - optional _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, + optional _plainRelativeTo_: *undefined* or a Temporal.PlainDate, + optional _zonedRelativeTo_: *undefined* or a Temporal.ZonedDateTime, ): either a normal completion containing a Record with fields [[DurationRecord]] (a Duration Record) and [[Total]] (a mathematical value), or a throw completion

@@ -1708,13 +1723,14 @@

- 1. If _relativeTo_ is not present, set _relativeTo_ to *undefined*. - 1. If _unit_ is *"year"*, *"month"*, or *"week"*, and _relativeTo_ is *undefined*, then + 1. If _plainRelativeTo_ is not present, set _plainRelativeTo_ to *undefined*. + 1. If _zonedRelativeTo_ is not present, set _zonedRelativeTo_ to *undefined*. + 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. If _relativeTo_ is an Object with an [[InitializedTemporalZonedDateTime]] internal slot, then - 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_relativeTo_, _years_, _months_, _weeks_, _days_). + 1. If _zonedRelativeTo_ is not *undefined*, then + 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_). 1. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). 1. Let _fractionalDays_ be _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]]. 1. Else, @@ -1726,85 +1742,82 @@

1. Assert: _fractionalDays_ is not used below. 1. Let _total_ be ~unset~. 1. If _unit_ is *"year"*, then - 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. Let _calendar_ be _plainRelativeTo_.[[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"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _yearsLater_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _yearsDuration_, *undefined*, _dateAdd_). + 1. Let _yearsLater_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _yearsDuration_, *undefined*, _dateAdd_). 1. Let _yearsMonthsWeeks_ be ! CreateTemporalDuration(_years_, _months_, _weeks_, 0, 0, 0, 0, 0, 0, 0). - 1. Let _yearsMonthsWeeksLater_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _yearsMonthsWeeks_, *undefined*, _dateAdd_). + 1. Let _yearsMonthsWeeksLater_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _yearsMonthsWeeks_, *undefined*, _dateAdd_). 1. Let _monthsWeeksInDays_ be DaysUntil(_yearsLater_, _yearsMonthsWeeksLater_). - 1. Set _relativeTo_ to _yearsLater_. + 1. Set _plainRelativeTo_ to _yearsLater_. 1. Set _fractionalDays_ to _fractionalDays_ + _monthsWeeksInDays_. - 1. Let _wholeDaysDuration_ be ? CreateTemporalDuration(0, 0, 0, truncate(_fractionalDays_), 0, 0, 0, 0, 0, 0). - 1. Let _wholeDaysLater_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _wholeDaysDuration_, *undefined*, _dateAdd_). + 1. Let _wholeDaysDuration_ be ? CreateTemporalDuration(0, 0, 0, truncate(_days_), 0, 0, 0, 0, 0, 0). + 1. Let _wholeDaysLater_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _wholeDaysDuration_, *undefined*, _dateAdd_). 1. Let _untilOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). - 1. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _wholeDaysLater_, _untilOptions_). + 1. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _plainRelativeTo_, _wholeDaysLater_, _untilOptions_). 1. Let _yearsPassed_ be _timePassed_.[[Years]]. 1. Set _years_ to _years_ + _yearsPassed_. 1. Let _yearsDuration_ be ! CreateTemporalDuration(_yearsPassed_, 0, 0, 0, 0, 0, 0, 0, 0, 0). - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _yearsDuration_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _yearsDuration_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _daysPassed_ be _moveResult_.[[Days]]. 1. Set _fractionalDays_ to _fractionalDays_ - _daysPassed_. 1. If _fractionalDays_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Let _oneYear_ be ! CreateTemporalDuration(_sign_, 0, 0, 0, 0, 0, 0, 0, 0, 0). - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneYear_, _dateAdd_). + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneYear_, _dateAdd_). 1. Let _oneYearDays_ be _moveResult_.[[Days]]. 1. Let _fractionalYears_ be _years_ + _fractionalDays_ / abs(_oneYearDays_). 1. Set _years_ to RoundNumberToIncrement(_fractionalYears_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalYears_. 1. Set _months_ and _weeks_ to 0. 1. Else if _unit_ is *"month"*, then - 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. Let _calendar_ be _plainRelativeTo_.[[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"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _yearsMonthsLater_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _yearsMonths_, *undefined*, _dateAdd_). + 1. Let _yearsMonthsLater_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _yearsMonths_, *undefined*, _dateAdd_). 1. Let _yearsMonthsWeeks_ be ! CreateTemporalDuration(_years_, _months_, _weeks_, 0, 0, 0, 0, 0, 0, 0). - 1. Let _yearsMonthsWeeksLater_ be ? CalendarDateAdd(_calendar_, _relativeTo_, _yearsMonthsWeeks_, *undefined*, _dateAdd_). + 1. Let _yearsMonthsWeeksLater_ be ? CalendarDateAdd(_calendar_, _plainRelativeTo_, _yearsMonthsWeeks_, *undefined*, _dateAdd_). 1. Let _weeksInDays_ be DaysUntil(_yearsMonthsLater_, _yearsMonthsWeeksLater_). - 1. Set _relativeTo_ to _yearsMonthsLater_. + 1. Set _plainRelativeTo_ to _yearsMonthsLater_. 1. Set _fractionalDays_ to _fractionalDays_ + _weeksInDays_. 1. If _fractionalDays_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Let _oneMonth_ be ! CreateTemporalDuration(0, _sign_, 0, 0, 0, 0, 0, 0, 0, 0). - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _oneMonthDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_fractionalDays_) ≥ abs(_oneMonthDays_), 1. Set _months_ to _months_ + _sign_. 1. Set _fractionalDays_ to _fractionalDays_ - _oneMonthDays_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneMonth_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneMonth_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneMonthDays_ to _moveResult_.[[Days]]. 1. Let _fractionalMonths_ be _months_ + _fractionalDays_ / abs(_oneMonthDays_). 1. Set _months_ to RoundNumberToIncrement(_fractionalMonths_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalMonths_. 1. Set _weeks_ to 0. 1. Else if _unit_ is *"week"*, then - 1. Set _relativeTo_ to ? ToTemporalDate(_relativeTo_). - 1. Let _calendar_ be _relativeTo_.[[Calendar]]. + 1. Let _calendar_ be _plainRelativeTo_.[[Calendar]]. 1. If _fractionalDays_ < 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 1. Let _dateAdd_ be ? GetMethod(_calendar_, *"dateAdd"*). 1. Else, 1. Let _dateAdd_ be ~unused~. - 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _relativeTo_, _oneWeek_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Let _moveResult_ be ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneWeek_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Let _oneWeekDays_ be _moveResult_.[[Days]]. 1. Repeat, while abs(_fractionalDays_) ≥ abs(_oneWeekDays_), 1. Set _weeks_ to _weeks_ + _sign_. 1. Set _fractionalDays_ to _fractionalDays_ - _oneWeekDays_. - 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _relativeTo_, _oneWeek_, _dateAdd_). - 1. Set _relativeTo_ to _moveResult_.[[RelativeTo]]. + 1. Set _moveResult_ to ? MoveRelativeDate(_calendar_, _plainRelativeTo_, _oneWeek_, _dateAdd_). + 1. Set _plainRelativeTo_ to _moveResult_.[[RelativeTo]]. 1. Set _oneWeekDays_ to _moveResult_.[[Days]]. 1. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_). 1. Set _weeks_ to RoundNumberToIncrement(_fractionalWeeks_, _increment_, _roundingMode_). @@ -1864,7 +1877,7 @@

_increment_: an integer, _unit_: a String, _roundingMode_: a String, - _relativeTo_: *undefined*, a Temporal.PlainDate, or a Temporal.ZonedDateTime, + _zonedRelativeTo_: a Temporal.ZonedDateTime, ): either a normal completion containing a Duration Record, or a throw completion

@@ -1876,19 +1889,19 @@

- 1. If Type(_relativeTo_) is not Object; or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + 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. If _timeRemainderNs_ = 0, let _direction_ be 0. 1. Else if _timeRemainderNs_ < 0, let _direction_ be -1. 1. Else, let _direction_ be 1. - 1. Let _dayStart_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). - 1. Let _dayEnd_ be ? AddZonedDateTime(_dayStart_, _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0). + 1. Let _dayStart_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). + 1. Let _dayEnd_ be ? AddZonedDateTime(_dayStart_, _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0). 1. Let _dayLengthNs_ be ℝ(_dayEnd_ - _dayStart_). 1. Let _oneDayLess_ be _timeRemainderNs_ - _dayLengthNs_. 1. If _oneDayLess_ × _direction_ < 0, then 1. Return ! CreateDurationRecord(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + 1. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, *undefined*, _zonedRelativeTo_). 1. Let _adjustedTimeDuration_ be ! RoundDuration(_adjustedDateDuration_.[[Years]], _adjustedDateDuration_.[[Months]], _adjustedDateDuration_.[[Weeks]], _adjustedDateDuration_.[[Days]], 0, 0, 0, 0, 0, _oneDayLess_, _increment_, _unit_, _roundingMode_). 1. Set _adjustedTimeDuration_ to ? BalanceTimeDuration(0, _adjustedTimeDuration_.[[Hours]], _adjustedTimeDuration_.[[Minutes]], _adjustedTimeDuration_.[[Seconds]], _adjustedTimeDuration_.[[Milliseconds]], _adjustedTimeDuration_.[[Microseconds]], _adjustedTimeDuration_.[[Nanoseconds]], *"hour"*). 1. Return ! CreateDurationRecord(_adjustedDateDuration_.[[Years]], _adjustedDateDuration_.[[Months]], _adjustedDateDuration_.[[Weeks]], _adjustedDateDuration_.[[Days]], _adjustedTimeDuration_.[[Hours]], _adjustedTimeDuration_.[[Minutes]], _adjustedTimeDuration_.[[Seconds]], _adjustedTimeDuration_.[[Milliseconds]], _adjustedTimeDuration_.[[Microseconds]], _adjustedTimeDuration_.[[Nanoseconds]]). @@ -1971,8 +1984,8 @@

1. If _operation_ is ~subtract~, let _sign_ be -1. Otherwise, let _sign_ be 1. 1. Set _other_ to ? ToTemporalDurationRecord(_other_). 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). - 1. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _sign_ × _other_.[[Years]], _sign_ × _other_.[[Months]], _sign_ × _other_.[[Weeks]], _sign_ × _other_.[[Days]], _sign_ × _other_.[[Hours]], _sign_ × _other_.[[Minutes]], _sign_ × _other_.[[Seconds]], _sign_ × _other_.[[Milliseconds]], _sign_ × _other_.[[Microseconds]], _sign_ × _other_.[[Nanoseconds]], _relativeTo_). + 1. Let _relativeToRecord_ be ? ToRelativeTemporalObject(_options_). + 1. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _sign_ × _other_.[[Years]], _sign_ × _other_.[[Months]], _sign_ × _other_.[[Weeks]], _sign_ × _other_.[[Days]], _sign_ × _other_.[[Hours]], _sign_ × _other_.[[Minutes]], _sign_ × _other_.[[Seconds]], _sign_ × _other_.[[Milliseconds]], _sign_ × _other_.[[Microseconds]], _sign_ × _other_.[[Nanoseconds]], _relativeToRecord_.[[PlainRelativeTo]], _relativeToRecord_.[[ZonedRelativeTo]]). 1. Return ! CreateTemporalDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index 8cf1e2ea3c..db120eedf4 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -1360,7 +1360,7 @@

description
- It converts a number of _nanoseconds_ relative to a Temporal.ZonedDateTime _relativeTo_, and converts it into a number of days and remainder of nanoseconds, taking into account any offset changes in the time zone of _relativeTo_. + It converts a number of _nanoseconds_ relative to a Temporal.ZonedDateTime _zonedRelativeTo_ (if supplied), and converts it into a number of days and remainder of nanoseconds, taking into account any offset changes in the time zone of _zonedRelativeTo_. It also returns the length of the last day in nanoseconds, for rounding purposes.
@@ -1370,12 +1370,12 @@

1. If _nanoseconds_ < 0, let _sign_ be -1; else, let _sign_ be 1. 1. Let _startNs_ be ℝ(_zonedRelativeTo_.[[Nanoseconds]]). 1. Let _startInstant_ be ! CreateTemporalInstant(ℤ(_startNs_)). - 1. Let _startDateTime_ be ? GetPlainDateTimeFor(_zonedRelativeTo_.[[TimeZone]], _startInstant_, _zonedRelativeTo_.[[Calendar]]). 1. Let _endNs_ be _startNs_ + _nanoseconds_. 1. If IsValidEpochNanoseconds(ℤ(_endNs_)) is *false*, throw a *RangeError* exception. 1. Let _endInstant_ be ! CreateTemporalInstant(ℤ(_endNs_)). + 1. Let _startDateTime_ be ? GetPlainDateTimeFor(_zonedRelativeTo_.[[TimeZone]], _startInstant_, _zonedRelativeTo_.[[Calendar]]). 1. Let _endDateTime_ be ? GetPlainDateTimeFor(_zonedRelativeTo_.[[TimeZone]], _endInstant_, _zonedRelativeTo_.[[Calendar]]). - 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*, OrdinaryObjectCreate(*null*)). + 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _zonedRelativeTo_.[[Calendar]], *"day"*, OrdinaryObjectCreate(*null*)). 1. Let _days_ be _dateDifference_.[[Days]]. 1. Let _intermediateNs_ be ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0)). 1. If _sign_ is 1, then @@ -1436,7 +1436,11 @@

1. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _settings_.[[LargestUnit]], _resolvedOptions_). 1. If _settings_.[[SmallestUnit]] is *"nanosecond"* and _settings_.[[RoundingIncrement]] is 1, then 1. Return ! CreateTemporalDuration(_sign_ × _difference_.[[Years]], _sign_ × _difference_.[[Months]], _sign_ × _difference_.[[Weeks]], _sign_ × _difference_.[[Days]], _sign_ × _difference_.[[Hours]], _sign_ × _difference_.[[Minutes]], _sign_ × _difference_.[[Seconds]], _sign_ × _difference_.[[Milliseconds]], _sign_ × _difference_.[[Microseconds]], _sign_ × _difference_.[[Nanoseconds]]). - 1. Let _roundRecord_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_). + 1. Let _plainRelativeTo_ be *undefined*. + 1. If _settings_.[[SmallestUnit]] is one of *"year"*, *"month"*, *"week"*, then + 1. NOTE: The above condition means that the corresponding `Temporal.PlainDate` for _zonedDateTime_ will be used in one of the operations below. + 1. Set _plainRelativeTo_ to ? ToTemporalDate(_zonedDateTime_). + 1. Let _roundRecord_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _plainRelativeTo_, _zonedDateTime_). 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. 1. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_). 1. Return ! CreateTemporalDuration(_sign_ × _result_.[[Years]], _sign_ × _result_.[[Months]], _sign_ × _result_.[[Weeks]], _sign_ × _result_.[[Days]], _sign_ × _result_.[[Hours]], _sign_ × _result_.[[Minutes]], _sign_ × _result_.[[Seconds]], _sign_ × _result_.[[Milliseconds]], _sign_ × _result_.[[Microseconds]], _sign_ × _result_.[[Nanoseconds]]). From 75456fa98fa09f0fe5ba880c0e022a4991b94bb3 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 7 Mar 2023 15:20:21 -0800 Subject: [PATCH 5/9] Normative: Don't observably iterate array in built-in calendar's fields() When calling CalendarFields with a built-in calendar, previously we would create a JS Array from the passed List, and pass it to %Temporal.Calendar. prototype.fields%, which would iterate it observably. So, instead we call CalendarDateFields when the calendar is a built-in calendar, which doesn't do anything observable at all. See: #2289 --- polyfill/lib/calendar.mjs | 7 +++++++ polyfill/lib/ecmascript.mjs | 5 ++--- spec/calendar.html | 9 ++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index 3d72741f04..a494a7bf94 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -2160,3 +2160,10 @@ impl['indian'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperIndian }); impl['buddhist'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperBuddhist }); impl['japanese'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperJapanese }); impl['gregory'] = ObjectAssign({}, nonIsoGeneralImpl, { helper: helperGregory }); + +function calendarFieldsImpl(calendar, fieldNames) { + return impl[calendar].fields(fieldNames); +} +// Probably not what the intrinsics mechanism was intended for, but view this as +// an export of calendarFieldsImpl while avoiding circular dependencies +DefineIntrinsic('calendarFieldsImpl', calendarFieldsImpl); diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 9c17e94113..33d358615f 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -1803,9 +1803,8 @@ export function CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar export function CalendarFields(calendar, fieldNames) { if (typeof calendar === 'string') { - const TemporalCalendar = GetIntrinsic('%Temporal.Calendar%'); - calendar = new TemporalCalendar(calendar); - return Call(GetIntrinsic('%Temporal.Calendar.prototype.fields%'), calendar, [fieldNames]); + if (calendar === 'iso8601') return fieldNames; + return GetIntrinsic('%calendarFieldsImpl%')(calendar, fieldNames); } const fields = GetMethod(calendar, 'fields'); fieldNames = Call(fields, calendar, [fieldNames]); diff --git a/spec/calendar.html b/spec/calendar.html index 42bca64e90..75fdf17ada 100644 --- a/spec/calendar.html +++ b/spec/calendar.html @@ -99,9 +99,12 @@

1. If _calendar_ is a String, then - 1. Set _calendar_ to ! CreateTemporalCalendar(_calendar_). - 1. Let _fieldsArray_ be ? Call(%Temporal.Calendar.prototype.fields%, _calendar_, « CreateArrayFromList(_fieldNames_) »). - 1. Return ! CreateListFromArrayLike(_fieldsArray_, « String »). + 1. Assert: _fieldNames_ contains no String other than *"year"*, *"month"*, *"monthCode"*, or *"day"*. + 1. If _calendar_ is *"iso8601"*, return _fieldNames_. + 1. Let _extraFieldDescriptors_ be CalendarFieldDescriptors(_calendar_, _fieldNames_). + 1. For each Calendar Field Descriptor Record _desc_ of _extraFieldDescriptors_, do + 1. Append _desc_.[[Property]] to _fieldNames_. + 1. Return _fieldNames_. 1. Let _fieldsArray_ be ? Invoke(_calendar_, *"fields"*, « CreateArrayFromList(_fieldNames_) »). 1. Let _iteratorRecord_ be ? GetIterator(_fieldsArray_, ~sync~). 1. Return ? IteratorToListOfType(_iteratorRecord_, « String »). From 91174470d04cab9d2b7ae783ff4d798213c5d2d7 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 7 Mar 2023 20:25:07 -0800 Subject: [PATCH 6/9] Normative: Avoid reconverting Zoned- to PlainDateTime in AddZonedDateTime There are several places where a ZonedDateTime is converted to a PlainDateTime, which is a user-visible time zone operation, and then the same operation is performed again right afterwards in AddZonedDateTime. Avoid this by making AddZonedDateTime take an optional precalculated PlainDateTime. If we have it, we can pass it in, and avoid the second conversion. --- polyfill/lib/ecmascript.mjs | 27 ++++++++++++++++++++++----- polyfill/lib/zoneddatetime.mjs | 15 ++------------- spec/zoneddatetime.html | 19 ++++++++++++------- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index 33d358615f..77b298d009 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -3338,7 +3338,7 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo) { 'day', ObjectCreate(null) ); - let intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days, 0, 0, 0, 0, 0, 0); + let intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days, 0, 0, 0, 0, 0, 0, dtStart); // may disambiguate // If clock time after addition was in the middle of a skipped period, the @@ -3353,7 +3353,22 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo) { if (sign === 1) { while (days.greater(0) && intermediateNs.greater(endNs)) { days = days.prev(); - intermediateNs = AddZonedDateTime(start, timeZone, calendar, 0, 0, 0, days.toJSNumber(), 0, 0, 0, 0, 0, 0); + intermediateNs = AddZonedDateTime( + start, + timeZone, + calendar, + 0, + 0, + 0, + days.toJSNumber(), + 0, + 0, + 0, + 0, + 0, + 0, + dtStart + ); // may do disambiguation } } @@ -4253,7 +4268,7 @@ export function DifferenceZonedDateTime(ns1, ns2, timeZone, calendar, largestUni largestUnit, options ); - let intermediateNs = AddZonedDateTime(start, timeZone, calendar, years, months, weeks, 0, 0, 0, 0, 0, 0, 0); + let intermediateNs = AddZonedDateTime(start, timeZone, calendar, years, months, weeks, 0, 0, 0, 0, 0, 0, 0, dtStart); // may disambiguate let timeRemainderNs = ns2.subtract(intermediateNs); const intermediate = CreateTemporalZonedDateTime(intermediateNs, timeZone, calendar); @@ -4947,7 +4962,8 @@ export function AddZonedDateTime( ms, µs, ns, - options + precalculatedPlainDateTime = undefined, + options = undefined ) { // If only time is to be added, then use Instant math. It's not OK to fall // through to the date/time code below because compatible disambiguation in @@ -4964,7 +4980,7 @@ export function AddZonedDateTime( // RFC 5545 requires the date portion to be added in calendar days and the // time portion to be added in exact time. - let dt = GetPlainDateTimeFor(timeZone, instant, calendar); + const dt = precalculatedPlainDateTime ?? GetPlainDateTimeFor(timeZone, instant, calendar); const datePart = CreateTemporalDate(GetSlot(dt, ISO_YEAR), GetSlot(dt, ISO_MONTH), GetSlot(dt, ISO_DAY), calendar); const dateDuration = new TemporalDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0); const addedDate = CalendarDateAdd(calendar, datePart, dateDuration, options); @@ -5170,6 +5186,7 @@ export function AddDurationToOrSubtractDurationFromZonedDateTime(operation, zone sign * milliseconds, sign * microseconds, sign * nanoseconds, + undefined, options ); return CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar); diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index 498f6114bc..60a6110d95 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -387,20 +387,9 @@ export class ZonedDateTime { let nanosecond = GetSlot(dt, ISO_NANOSECOND); const calendar = GetSlot(this, CALENDAR); - const dtStart = ES.CreateTemporalDateTime( - GetSlot(dt, ISO_YEAR), - GetSlot(dt, ISO_MONTH), - GetSlot(dt, ISO_DAY), - 0, - 0, - 0, - 0, - 0, - 0, - 'iso8601' - ); + const dtStart = ES.CreateTemporalDateTime(year, month, day, 0, 0, 0, 0, 0, 0, 'iso8601'); const instantStart = ES.GetInstantFor(timeZone, dtStart, 'compatible'); - const endNs = ES.AddZonedDateTime(instantStart, timeZone, calendar, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0); + const endNs = ES.AddZonedDateTime(instantStart, timeZone, calendar, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, dtStart); const dayLengthNs = endNs.subtract(GetSlot(instantStart, EPOCHNANOSECONDS)); if (dayLengthNs.leq(0)) { throw new RangeError('cannot round a ZonedDateTime in a calendar with zero- or negative-length days'); diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index db120eedf4..364538fd68 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -768,7 +768,7 @@

Temporal.ZonedDateTime.prototype.round ( _roundTo_ )

1. Let _dtStart_ be ? CreateTemporalDateTime(_temporalDateTime_.[[ISOYear]], _temporalDateTime_.[[ISOMonth]], _temporalDateTime_.[[ISODay]], 0, 0, 0, 0, 0, 0, *"iso8601"*). 1. Let _instantStart_ be ? GetInstantFor(_timeZone_, _dtStart_, *"compatible"*). 1. Let _startNs_ be _instantStart_.[[Nanoseconds]]. - 1. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _calendar_, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0). + 1. Let _endNs_ be ? AddZonedDateTime(_startNs_, _timeZone_, _calendar_, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, _dtStart_). 1. Let _dayLengthNs_ be ℝ(_endNs_ - _startNs_). 1. If _dayLengthNs_ ≤ 0, then 1. Throw a *RangeError* exception. @@ -1290,6 +1290,7 @@

_milliseconds_: an integer, _microseconds_: an integer, _nanoseconds_: an integer, + optional _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, optional _options_: an Object, ): either a normal completion containing a BigInt or an abrupt completion

@@ -1300,13 +1301,17 @@

As specified in RFC 5545, the date portion of the duration is added in calendar days, and the time portion is added in exact time.

+

Unless _precalculatedPlainDateTime_ is supplied, the given _timeZone_'s `getOffsetNanosecondsFor` method will be called to convert _epochNanoseconds_ to a wall-clock time.

1. If _options_ is not present, set _options_ to *undefined*. 1. Assert: Type(_options_) is Object or Undefined. 1. If _years_ = 0, _months_ = 0, _weeks_ = 0, and _days_ = 0, then 1. Return ? AddInstant(_epochNanoseconds_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Let _instant_ be ! CreateTemporalInstant(_epochNanoseconds_). - 1. Let _temporalDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). + 1. If _precalculatedPlainDateTime_ is not *undefined*, then + 1. Let _temporalDateTime_ be _precalculatedPlainDateTime_. + 1. Else, + 1. Let _instant_ be ! CreateTemporalInstant(_epochNanoseconds_). + 1. Let _temporalDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _instant_, _calendar_). 1. Let _datePart_ be ! CreateTemporalDate(_temporalDateTime_.[[ISOYear]], _temporalDateTime_.[[ISOMonth]], _temporalDateTime_.[[ISODay]], _calendar_). 1. Let _dateDuration_ be ! CreateTemporalDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). 1. Let _addedDate_ be ? CalendarDateAdd(_calendar_, _datePart_, _dateDuration_, _options_). @@ -1341,7 +1346,7 @@

1. Let _endInstant_ be ! CreateTemporalInstant(_ns2_). 1. Let _endDateTime_ be ? GetPlainDateTimeFor(_timeZone_, _endInstant_, _calendar_). 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). - 1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0). + 1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0, _startDateTime_). 1. Let _timeRemainderNs_ be _ns2_ - _intermediateNs_. 1. Let _intermediate_ be ! CreateTemporalZonedDateTime(_intermediateNs_, _timeZone_, _calendar_). 1. Let _result_ be ? NanosecondsToDays(ℝ(_timeRemainderNs_), _intermediate_). @@ -1377,11 +1382,11 @@

1. Let _endDateTime_ be ? GetPlainDateTimeFor(_zonedRelativeTo_.[[TimeZone]], _endInstant_, _zonedRelativeTo_.[[Calendar]]). 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _zonedRelativeTo_.[[Calendar]], *"day"*, OrdinaryObjectCreate(*null*)). 1. Let _days_ be _dateDifference_.[[Days]]. - 1. Let _intermediateNs_ be ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0)). + 1. Let _intermediateNs_ be ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0, _startDateTime_)). 1. If _sign_ is 1, then 1. Repeat, while _days_ > 0 and _intermediateNs_ > _endNs_, 1. Set _days_ to _days_ - 1. - 1. Set _intermediateNs_ to ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0)). + 1. Set _intermediateNs_ to ℝ(? AddZonedDateTime(ℤ(_startNs_), _zonedRelativeTo_.[[TimeZone]], _zonedRelativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0, _startDateTime_)). 1. Set _nanoseconds_ to _endNs_ - _intermediateNs_. 1. Let _done_ be *false*. 1. Let _dayLengthNs_ be ~unset~. @@ -1465,7 +1470,7 @@

1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _timeZone_ be _zonedDateTime_.[[TimeZone]]. 1. Let _calendar_ be _zonedDateTime_.[[Calendar]]. - 1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _sign_ × _duration_.[[Years]], _sign_ × _duration_.[[Months]], _sign_ × _duration_.[[Weeks]], _sign_ × _duration_.[[Days]], _sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]], _options_). + 1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZone_, _calendar_, _sign_ × _duration_.[[Years]], _sign_ × _duration_.[[Months]], _sign_ × _duration_.[[Weeks]], _sign_ × _duration_.[[Days]], _sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]], *undefined*, _options_). 1. Return ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZone_, _calendar_). From 4c6d280274237c56db834747c513f6430aa18267 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Fri, 10 Mar 2023 09:38:42 -0800 Subject: [PATCH 7/9] Normative: Return early from Duration.compare if internal slots equal If all the fields are equal, then no balancing has to happen, and the relativeTo point doesn't matter; we know the durations are equal. --- polyfill/lib/duration.mjs | 18 +++++++++++++++++- spec/duration.html | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 27e640a7db..077a6fece6 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -579,7 +579,6 @@ export class Duration { one = ES.ToTemporalDuration(one); two = ES.ToTemporalDuration(two); options = ES.GetOptionsObject(options); - let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); const y1 = GetSlot(one, YEARS); const mon1 = GetSlot(one, MONTHS); const w1 = GetSlot(one, WEEKS); @@ -600,6 +599,23 @@ export class Duration { const ms2 = GetSlot(two, MILLISECONDS); const µs2 = GetSlot(two, MICROSECONDS); let ns2 = GetSlot(two, NANOSECONDS); + + if ( + y1 === y2 && + mon1 === mon2 && + w1 === w2 && + d1 === d2 && + h1 === h2 && + min1 === min2 && + s1 === s2 && + ms1 === ms2 && + µs1 === µs2 && + ns1 === ns2 + ) { + return 0; + } + let { 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) { diff --git a/spec/duration.html b/spec/duration.html index 8e1b9bea71..879b6a3997 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -94,6 +94,8 @@

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

1. Set _one_ to ? ToTemporalDuration(_one_). 1. Set _two_ to ? ToTemporalDuration(_two_). 1. Set _options_ to ? GetOptionsObject(_options_). + 1. If _one_.[[Years]] = _two_.[[Years]], and _one_.[[Months]] = _two_.[[Months]], and _one_.[[Weeks]] = _two_.[[Weeks]], and _one_.[[Days]] = _two_.[[Days]], and _one_.[[Hours]] = _two_.[[Hours]], and _one_.[[Minutes]] = _two_.[[Minutes]], and _one_.[[Seconds]] = _two_.[[Seconds]], and _one_.[[Millieconds]] = _two_.[[Millieconds]], and _one_.[[Microseconds]] = _two_.[[Microseconds]], and _one_.[[Nanoseconds]] = _two_.[[Nanoseconds]], then + 1. Return *+0*𝔽. 1. Let _relativeToRecord_ be ? ToRelativeTemporalObject(_options_). 1. Let _zonedRelativeTo_ be _relativeToRecord_.[[ZonedRelativeTo]]. 1. Let _plainRelativeTo_ be _relativeToRecord_.[[PlainRelativeTo]]. From ba0400cbb69b6334e664928b2b0563cd90d651f5 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Tue, 7 Mar 2023 21:01:35 -0800 Subject: [PATCH 8/9] Normative: Do away with CalculateOffsetShift in Duration.compare CalculateOffsetShift is only used in Duration.compare, when relativeTo is a ZonedDateTime. Calling it twice performs two ZonedDateTime additions, which are potentially user-observable method calls. If we are performing these two additions anyway, we may as well just add each duration to relativeTo and compare the timestamps. This saves a lot of other user-observable calls. It also allows removing the offset-shift parameter from TotalDurationNanoseconds, because now it is always zero. That parameter was confusing anyway. At the same time we remove the days parameter as well, because now there is no indication that days should only be 24-hour days; we push the responsibility of deciding whether 24-hour days are appropriate to the caller. --- polyfill/lib/duration.mjs | 66 ++++++++++++++++++++++++++++++++----- polyfill/lib/ecmascript.mjs | 48 ++++----------------------- spec/duration.html | 66 ++++++++++++++----------------------- 3 files changed, 89 insertions(+), 91 deletions(-) diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 077a6fece6..2e618b01d6 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -1,5 +1,7 @@ /* global __debug__ */ +import bigInt from 'big-integer'; + import * as ES from './ecmascript.mjs'; import { MakeIntrinsicClass } from './intrinsicclass.mjs'; import { @@ -13,6 +15,9 @@ import { MILLISECONDS, MICROSECONDS, NANOSECONDS, + CALENDAR, + INSTANT, + TIME_ZONE, CreateSlots, GetSlot, SetSlot @@ -583,7 +588,7 @@ export class Duration { const mon1 = GetSlot(one, MONTHS); const w1 = GetSlot(one, WEEKS); let d1 = GetSlot(one, DAYS); - const h1 = GetSlot(one, HOURS); + let h1 = GetSlot(one, HOURS); const min1 = GetSlot(one, MINUTES); const s1 = GetSlot(one, SECONDS); const ms1 = GetSlot(one, MILLISECONDS); @@ -593,7 +598,7 @@ export class Duration { const mon2 = GetSlot(two, MONTHS); const w2 = GetSlot(two, WEEKS); let d2 = GetSlot(two, DAYS); - const h2 = GetSlot(two, HOURS); + let h2 = GetSlot(two, HOURS); const min2 = GetSlot(two, MINUTES); const s2 = GetSlot(two, SECONDS); const ms2 = GetSlot(two, MILLISECONDS); @@ -614,17 +619,60 @@ export class Duration { ) { return 0; } - let { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); + const { plainRelativeTo, zonedRelativeTo } = ES.ToRelativeTemporalObject(options); + + 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 precalculatedPlainDateTime = ES.GetPlainDateTimeFor(timeZone, instant, calendar); + + const after1 = ES.AddZonedDateTime( + instant, + timeZone, + calendar, + y1, + mon1, + w1, + d1, + h1, + min1, + s1, + ms1, + µs1, + ns1, + precalculatedPlainDateTime + ); + const after2 = ES.AddZonedDateTime( + instant, + timeZone, + calendar, + y2, + mon2, + w2, + d2, + h2, + min2, + s2, + ms2, + µs2, + ns2, + precalculatedPlainDateTime + ); + return ES.ComparisonResult(after1.minus(after2).toJSNumber()); + } - 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); + 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); + h1 = bigInt(h1).add(bigInt(d1).multiply(24)); + h2 = bigInt(h2).add(bigInt(d2).multiply(24)); + ns1 = ES.TotalDurationNanoseconds(h1, min1, s1, ms1, µs1, ns1); + ns2 = ES.TotalDurationNanoseconds(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 77b298d009..f7622d99c4 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -2607,7 +2607,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, 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)); @@ -3280,19 +3280,8 @@ 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); - hours = bigInt(hours).add(bigInt(days).multiply(24)); - minutes = bigInt(minutes).add(hours.multiply(60)); +export function TotalDurationNanoseconds(hours, minutes, seconds, milliseconds, microseconds, nanoseconds) { + minutes = bigInt(minutes).add(bigInt(hours).multiply(60)); seconds = bigInt(seconds).add(minutes.multiply(60)); milliseconds = bigInt(milliseconds).add(seconds.multiply(1000)); microseconds = bigInt(microseconds).add(milliseconds.multiply(1000)); @@ -3441,7 +3430,8 @@ export function BalancePossiblyInfiniteTimeDuration( nanoseconds, largestUnit ) { - nanoseconds = TotalDurationNanoseconds(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 0); + hours = bigInt(hours).add(bigInt(days).multiply(24)); + nanoseconds = TotalDurationNanoseconds(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); const sign = nanoseconds.lesser(0) ? -1 : 1; nanoseconds = nanoseconds.abs(); @@ -3865,21 +3855,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( @@ -5401,16 +5376,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(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); const direction = MathSign(timeRemainderNs.toJSNumber()); const timeZone = GetSlot(zonedRelativeTo, TIME_ZONE); @@ -5530,7 +5496,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(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..d654773334 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 _precalculatedPlainDateTime_ 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]], _precalculatedPlainDateTime_). + 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]], _precalculatedPlainDateTime_). + 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,10 @@

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 _hours1_ be _one_.[[Hours]] + _days1_ × 24. + 1. Let _hours2_ be _two_.[[Hours]] + _days2_ × 24. + 1. Let _ns1_ be TotalDurationNanoseconds(_hours1_, _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]]). + 1. Let _ns2_ be TotalDurationNanoseconds(_hours2_, _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,52 +1147,24 @@

- -

- 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 ( - _days_: an integer, _hours_: an integer, _minutes_: an integer, _seconds_: an integer, _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. +
- 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. 1. Set _milliseconds_ to _milliseconds_ + _seconds_ × 1000. @@ -1234,7 +1217,8 @@

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 _hours_ to _hours_ + _days_ × 24. + 1. Set _nanoseconds_ to TotalDurationNanoseconds(_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(_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(_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. From 0455ddf6931ba19b1a8fa6bde82a94c9e3cc68a1 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 13 Sep 2023 11:00:21 +0200 Subject: [PATCH 9/9] Update test262 --- polyfill/test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/test262 b/polyfill/test262 index bdddd9e2d2..e98bfb332e 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit bdddd9e2d286600462958c7bac6af10a2946134b +Subproject commit e98bfb332e653a5c8c38763574140dae1417b7a8