diff --git a/polyfill/lib/calendar.mjs b/polyfill/lib/calendar.mjs index a494a7bf94..deb13d7170 100644 --- a/polyfill/lib/calendar.mjs +++ b/polyfill/lib/calendar.mjs @@ -22,6 +22,7 @@ import { HasSlot, SetSlot } from './slots.mjs'; +import { TimeDuration } from './timeduration.mjs'; const ArrayFrom = Array.from; const ArrayIncludes = Array.prototype.includes; @@ -157,16 +158,15 @@ export class Calendar { duration = ES.ToTemporalDuration(duration); options = ES.GetOptionsObject(options); const overflow = ES.ToTemporalOverflow(options); - const { days } = ES.BalanceTimeDuration( - GetSlot(duration, DAYS), + const norm = TimeDuration.normalize( GetSlot(duration, HOURS), GetSlot(duration, MINUTES), GetSlot(duration, SECONDS), GetSlot(duration, MILLISECONDS), GetSlot(duration, MICROSECONDS), - GetSlot(duration, NANOSECONDS), - 'day' + GetSlot(duration, NANOSECONDS) ); + const days = GetSlot(duration, DAYS) + ES.BalanceTimeDuration(norm, 'day').days; const id = GetSlot(this, CALENDAR_ID); return impl[id].dateAdd( date, diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 77d44744b9..6af9c6c571 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -1,7 +1,5 @@ /* global __debug__ */ -import bigInt from 'big-integer'; - import * as ES from './ecmascript.mjs'; import { MakeIntrinsicClass } from './intrinsicclass.mjs'; import { CalendarMethodRecord } from './methodrecord.mjs'; @@ -18,10 +16,12 @@ import { NANOSECONDS, CALENDAR, INSTANT, + EPOCHNANOSECONDS, CreateSlots, GetSlot, SetSlot } from './slots.mjs'; +import { TimeDuration } from './timeduration.mjs'; const MathAbs = Math.abs; const ObjectCreate = Object.create; @@ -65,6 +65,7 @@ export class Duration { SetSlot(this, NANOSECONDS, nanoseconds); if (typeof __debug__ !== 'undefined' && __debug__) { + const normSeconds = TimeDuration.normalize(0, 0, seconds, milliseconds, microseconds, nanoseconds); Object.defineProperty(this, '_repr_', { value: `Temporal.Duration <${ES.TemporalDurationToString( years, @@ -73,10 +74,7 @@ export class Duration { days, hours, minutes, - seconds, - milliseconds, - microseconds, - nanoseconds + normSeconds )}>`, writable: false, enumerable: false, @@ -364,56 +362,40 @@ export class Duration { plainRelativeTo, calendarRec )); - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - ES.RoundDuration( + let norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + ({ years, months, weeks, days, norm } = ES.RoundDuration( + years, + months, + weeks, + days, + norm, + roundingIncrement, + smallestUnit, + roundingMode, + plainRelativeTo, + calendarRec, + zonedRelativeTo, + timeZoneRec, + precalculatedPlainDateTime + )); + if (zonedRelativeTo) { + ({ years, months, weeks, days, norm } = ES.AdjustRoundedDurationDays( years, months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, roundingIncrement, smallestUnit, roundingMode, - plainRelativeTo, - calendarRec, zonedRelativeTo, + calendarRec, timeZoneRec, precalculatedPlainDateTime )); - if (zonedRelativeTo) { - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - ES.AdjustRoundedDurationDays( - years, - months, - weeks, - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - roundingIncrement, - smallestUnit, - roundingMode, - zonedRelativeTo, - calendarRec, - timeZoneRec, - precalculatedPlainDateTime - )); ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDurationRelative( days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, largestUnit, zonedRelativeTo, timeZoneRec, @@ -421,13 +403,7 @@ export class Duration { )); } else { ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm.add24HourDays(days), largestUnit )); } @@ -514,6 +490,7 @@ export class Duration { plainRelativeTo, calendarRec )); + let norm; // If the unit we're totalling is smaller than `days`, convert days down to that unit. if (zonedRelativeTo) { const intermediate = ES.MoveRelativeZonedDateTime( @@ -526,29 +503,29 @@ export class Duration { 0, precalculatedPlainDateTime ); - ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDurationRelative( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - unit, - intermediate, - timeZoneRec - )); + norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + + // Inline BalanceTimeDurationRelative, without the final balance step + const start = GetSlot(intermediate, INSTANT); + const startNs = GetSlot(intermediate, EPOCHNANOSECONDS); + let intermediateNs = startNs; + let startDt; + if (days !== 0) { + startDt = ES.GetPlainDateTimeFor(timeZoneRec, start, 'iso8601'); + intermediateNs = ES.AddDaysToZonedDateTime(start, startDt, timeZoneRec, 'iso8601', days).epochNs; + } + const endNs = ES.AddInstant(intermediateNs, norm); + norm = TimeDuration.fromEpochNsDiff(endNs, startNs); + if (unit === 'year' || unit === 'month' || unit === 'week' || unit === 'day') { + if (!norm.isZero()) startDt ??= ES.GetPlainDateTimeFor(timeZoneRec, start, 'iso8601'); + ({ days, norm } = ES.NormalizedTimeDurationToDays(norm, intermediate, timeZoneRec, startDt)); + } else { + days = 0; + } } else { - ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - unit - )); + norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + norm = norm.add24HourDays(days); + days = 0; } // Finally, truncate to the correct unit and calculate remainder const { total } = ES.RoundDuration( @@ -556,12 +533,7 @@ export class Duration { months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, 1, unit, 'trunc', @@ -596,6 +568,7 @@ export class Duration { let nanoseconds = GetSlot(this, NANOSECONDS); if (unit !== 'nanosecond' || increment !== 1) { + let norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); const largestUnit = ES.DefaultTemporalLargestUnit( years, months, @@ -608,49 +581,33 @@ export class Duration { microseconds, nanoseconds ); - ({ seconds, milliseconds, microseconds, nanoseconds } = ES.RoundDuration( - 0, - 0, - 0, - 0, - 0, - 0, - seconds, - milliseconds, - microseconds, - nanoseconds, - increment, - unit, - roundingMode - )); - ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceTimeDuration( - days, + ({ norm } = ES.RoundDuration(0, 0, 0, 0, norm, increment, unit, roundingMode)); + let deltaDays; + ({ + days: deltaDays, hours, minutes, seconds, milliseconds, microseconds, - nanoseconds, - largestUnit - )); + nanoseconds + } = ES.BalanceTimeDuration(norm, largestUnit)); + days += deltaDays; } - return ES.TemporalDurationToString( - years, - months, - weeks, - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - precision - ); + const normSeconds = TimeDuration.normalize(0, 0, seconds, milliseconds, microseconds, nanoseconds); + return ES.TemporalDurationToString(years, months, weeks, days, hours, minutes, normSeconds, precision); } toJSON() { if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver'); + const normSeconds = TimeDuration.normalize( + 0, + 0, + GetSlot(this, SECONDS), + GetSlot(this, MILLISECONDS), + GetSlot(this, MICROSECONDS), + GetSlot(this, NANOSECONDS) + ); return ES.TemporalDurationToString( GetSlot(this, YEARS), GetSlot(this, MONTHS), @@ -658,10 +615,7 @@ export class Duration { GetSlot(this, DAYS), GetSlot(this, HOURS), GetSlot(this, MINUTES), - GetSlot(this, SECONDS), - GetSlot(this, MILLISECONDS), - GetSlot(this, MICROSECONDS), - GetSlot(this, NANOSECONDS) + normSeconds ); } toLocaleString(locales = undefined, options = undefined) { @@ -670,6 +624,14 @@ export class Duration { return new Intl.DurationFormat(locales, options).format(this); } console.warn('Temporal.Duration.prototype.toLocaleString() requires Intl.DurationFormat.'); + const normSeconds = TimeDuration.normalize( + 0, + 0, + GetSlot(this, SECONDS), + GetSlot(this, MILLISECONDS), + GetSlot(this, MICROSECONDS), + GetSlot(this, NANOSECONDS) + ); return ES.TemporalDurationToString( GetSlot(this, YEARS), GetSlot(this, MONTHS), @@ -677,10 +639,7 @@ export class Duration { GetSlot(this, DAYS), GetSlot(this, HOURS), GetSlot(this, MINUTES), - GetSlot(this, SECONDS), - GetSlot(this, MILLISECONDS), - GetSlot(this, MICROSECONDS), - GetSlot(this, NANOSECONDS) + normSeconds ); } valueOf() { @@ -756,6 +715,8 @@ export class Duration { const instant = GetSlot(zonedRelativeTo, INSTANT); const precalculatedPlainDateTime = ES.GetPlainDateTimeFor(timeZoneRec, instant, calendarRec.receiver); + const norm1 = TimeDuration.normalize(h1, min1, s1, ms1, µs1, ns1); + const norm2 = TimeDuration.normalize(h2, min2, s2, ms2, µs2, ns2); const after1 = ES.AddZonedDateTime( instant, timeZoneRec, @@ -764,12 +725,7 @@ export class Duration { mon1, w1, d1, - h1, - min1, - s1, - ms1, - µs1, - ns1, + norm1, precalculatedPlainDateTime ); const after2 = ES.AddZonedDateTime( @@ -780,12 +736,7 @@ export class Duration { mon2, w2, d2, - h2, - min2, - s2, - ms2, - µs2, - ns2, + norm2, precalculatedPlainDateTime ); return ES.ComparisonResult(after1.minus(after2).toJSNumber()); @@ -796,11 +747,9 @@ export class Duration { ({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', plainRelativeTo, calendarRec)); ({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', plainRelativeTo, calendarRec)); } - 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()); + const norm1 = TimeDuration.normalize(h1, min1, s1, ms1, µs1, ns1).add24HourDays(d1); + const norm2 = TimeDuration.normalize(h2, min2, s2, ms2, µs2, ns2).add24HourDays(d2); + return norm1.cmp(norm2); } } diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index ea3cd24a1d..ed5ab511c9 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -60,6 +60,7 @@ import some from 'es-abstract/helpers/some.js'; import { GetIntrinsic } from './intrinsicclass.mjs'; import { CalendarMethodRecord, TimeZoneMethodRecord } from './methodrecord.mjs'; +import { TimeDuration } from './timeduration.mjs'; import { CreateSlots, GetSlot, @@ -682,6 +683,7 @@ export function ParseTemporalDurationString(isoString) { seconds += MathTrunc(excessNanoseconds / 1e9) % 60; minutes += MathTrunc(excessNanoseconds / 60e9); + RejectDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; } @@ -2437,20 +2439,8 @@ export function DisambiguatePossibleInstants(possibleInstants, timeZoneRec, date const nanoseconds = offsetAfter - offsetBefore; switch (disambiguation) { case 'earlier': { - const earlierTime = AddTime( - hour, - minute, - second, - millisecond, - microsecond, - nanosecond, - 0, - 0, - 0, - 0, - 0, - -nanoseconds - ); + const norm = TimeDuration.normalize(0, 0, 0, 0, 0, -nanoseconds); + const earlierTime = AddTime(hour, minute, second, millisecond, microsecond, nanosecond, norm); const earlierDate = AddISODate(year, month, day, 0, 0, 0, earlierTime.deltaDays, 'constrain'); const earlierPlainDateTime = CreateTemporalDateTime( earlierDate.year, @@ -2468,7 +2458,8 @@ export function DisambiguatePossibleInstants(possibleInstants, timeZoneRec, date case 'compatible': // fall through because 'compatible' means 'later' for "spring forward" transitions case 'later': { - const laterTime = AddTime(hour, minute, second, millisecond, microsecond, nanosecond, 0, 0, 0, 0, 0, nanoseconds); + const norm = TimeDuration.normalize(0, 0, 0, 0, 0, nanoseconds); + const laterTime = AddTime(hour, minute, second, millisecond, microsecond, nanosecond, norm); const laterDate = AddISODate(year, month, day, 0, 0, 0, laterTime.deltaDays, 'constrain'); const laterPlainDateTime = CreateTemporalDateTime( laterDate.year, @@ -2565,25 +2556,8 @@ function formatAsDecimalNumber(num) { return bigInt(num).toString(); } -export function TemporalDurationToString( - years, - months, - weeks, - days, - hours, - minutes, - seconds, - ms, - µs, - ns, - precision = 'auto' -) { - const sign = DurationSign(years, months, weeks, days, hours, minutes, seconds, ms, µs, ns); - - 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)); +export function TemporalDurationToString(years, months, weeks, days, hours, minutes, normSeconds, precision = 'auto') { + const sign = DurationSign(years, months, weeks, days, hours, minutes, normSeconds.sec, 0, 0, normSeconds.subsec); let datePart = ''; if (years !== 0) datePart += `${formatAsDecimalNumber(MathAbs(years))}Y`; @@ -2596,17 +2570,12 @@ export function TemporalDurationToString( if (minutes !== 0) timePart += `${formatAsDecimalNumber(MathAbs(minutes))}M`; if ( - !seconds.isZero() || - !ms.isZero() || - !µs.isZero() || - !ns.isZero() || + !normSeconds.isZero() || (years === 0 && months === 0 && weeks === 0 && days === 0 && hours === 0 && minutes === 0) || precision !== 'auto' ) { - const secondsPart = formatAsDecimalNumber(seconds.abs()); - const subSecondNanoseconds = - MathAbs(ms.toJSNumber()) * 1e6 + MathAbs(µs.toJSNumber()) * 1e3 + MathAbs(ns.toJSNumber()); - const subSecondsPart = FormatFractionalSeconds(subSecondNanoseconds, precision); + const secondsPart = formatAsDecimalNumber(MathAbs(normSeconds.sec)); + const subSecondsPart = FormatFractionalSeconds(MathAbs(normSeconds.subsec), precision); timePart += `${secondsPart}${subSecondsPart}S`; } let result = `${sign < 0 ? '-' : ''}P${datePart}`; @@ -3254,24 +3223,15 @@ export function BalanceTime(hour, minute, second, millisecond, microsecond, nano }; } -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)); - return bigInt(nanoseconds).add(microseconds.multiply(1000)); -} - -export function NanosecondsToDays(nanoseconds, zonedRelativeTo, timeZoneRec, precalculatedPlainDateTime) { +export function NormalizedTimeDurationToDays(norm, zonedRelativeTo, timeZoneRec, precalculatedPlainDateTime) { // getOffsetNanosecondsFor and getPossibleInstantsFor must be looked up const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); - const sign = MathSign(nanoseconds); - nanoseconds = bigInt(nanoseconds); - if (sign === 0) return { days: 0, nanoseconds: bigInt.zero, dayLengthNs: DAY_NANOS.toJSNumber() }; + const sign = norm.sign(); + if (sign === 0) return { days: 0, norm, dayLengthNs: DAY_NANOS }; const startNs = GetSlot(zonedRelativeTo, EPOCHNANOSECONDS); const start = GetSlot(zonedRelativeTo, INSTANT); - const endNs = startNs.add(nanoseconds); + const endNs = norm.addToEpochNs(startNs); const end = new TemporalInstant(endNs); const calendar = GetSlot(zonedRelativeTo, CALENDAR); @@ -3322,7 +3282,7 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo, timeZoneRec, pre // may do disambiguation } } - nanoseconds = endNs.subtract(relativeResult.epochNs); + norm = TimeDuration.fromEpochNsDiff(endNs, relativeResult.epochNs); let isOverflow = false; let dayLengthNs; @@ -3336,10 +3296,11 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo, timeZoneRec, pre sign ); - dayLengthNs = oneDayFarther.epochNs.subtract(relativeResult.epochNs).toJSNumber(); - isOverflow = nanoseconds.subtract(dayLengthNs).multiply(sign).geq(0); + dayLengthNs = TimeDuration.fromEpochNsDiff(oneDayFarther.epochNs, relativeResult.epochNs); + const oneDayLess = norm.subtract(dayLengthNs); + isOverflow = oneDayLess.sign() * sign >= 0; if (isOverflow) { - nanoseconds = nanoseconds.subtract(dayLengthNs); + norm = oneDayLess; relativeResult = oneDayFarther; days += sign; } @@ -3347,85 +3308,103 @@ export function NanosecondsToDays(nanoseconds, zonedRelativeTo, timeZoneRec, pre if (days !== 0 && MathSign(days) != sign) { throw new RangeError('Time zone or calendar converted nanoseconds into a number of days with the opposite sign'); } - if (!nanoseconds.isZero() && MathSign(nanoseconds.toJSNumber()) != sign) { - if (nanoseconds.lt(0) && sign === 1) { + if (!norm.isZero() && norm.sign() !== sign) { + if (norm.sign() === -1 && sign === 1) { throw new Error('assert not reached'); } throw new RangeError('Time zone or calendar ended up with a remainder of nanoseconds with the opposite sign'); } - if (nanoseconds.abs().geq(MathAbs(dayLengthNs))) { + if (norm.abs().cmp(dayLengthNs.abs()) >= 0) { throw new Error('assert not reached'); } - return { days, nanoseconds, dayLengthNs: MathAbs(dayLengthNs) }; + return { days, norm, dayLengthNs: dayLengthNs.abs().totalNs }; } -export function BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - largestUnit -) { - 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(); - microseconds = milliseconds = seconds = minutes = hours = days = bigInt.zero; +export function BalanceTimeDuration(norm, largestUnit) { + const sign = norm.sign(); + let nanoseconds = norm.abs().subsec; + let microseconds = 0; + let milliseconds = 0; + let seconds = norm.abs().sec; + let minutes = 0; + let hours = 0; + let days = 0; switch (largestUnit) { case 'year': case 'month': case 'week': case 'day': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); - ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); - ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); - ({ quotient: minutes, remainder: seconds } = seconds.divmod(60)); - ({ quotient: hours, remainder: minutes } = minutes.divmod(60)); - ({ quotient: days, remainder: hours } = hours.divmod(24)); + microseconds = MathTrunc(nanoseconds / 1000); + nanoseconds %= 1000; + milliseconds = MathTrunc(microseconds / 1000); + microseconds %= 1000; + seconds += MathTrunc(milliseconds / 1000); + milliseconds %= 1000; + minutes = MathTrunc(seconds / 60); + seconds %= 60; + hours = MathTrunc(minutes / 60); + minutes %= 60; + days = MathTrunc(hours / 24); + hours %= 24; break; case 'hour': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); - ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); - ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); - ({ quotient: minutes, remainder: seconds } = seconds.divmod(60)); - ({ quotient: hours, remainder: minutes } = minutes.divmod(60)); + microseconds = MathTrunc(nanoseconds / 1000); + nanoseconds %= 1000; + milliseconds = MathTrunc(microseconds / 1000); + microseconds %= 1000; + seconds += MathTrunc(milliseconds / 1000); + milliseconds %= 1000; + minutes = MathTrunc(seconds / 60); + seconds %= 60; + hours = MathTrunc(minutes / 60); + minutes %= 60; break; case 'minute': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); - ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); - ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); - ({ quotient: minutes, remainder: seconds } = seconds.divmod(60)); + microseconds = MathTrunc(nanoseconds / 1000); + nanoseconds %= 1000; + milliseconds = MathTrunc(microseconds / 1000); + microseconds %= 1000; + seconds += MathTrunc(milliseconds / 1000); + milliseconds %= 1000; + minutes = MathTrunc(seconds / 60); + seconds %= 60; break; case 'second': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); - ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); - ({ quotient: seconds, remainder: milliseconds } = milliseconds.divmod(1000)); + microseconds = MathTrunc(nanoseconds / 1000); + nanoseconds %= 1000; + milliseconds = MathTrunc(microseconds / 1000); + microseconds %= 1000; + seconds += MathTrunc(milliseconds / 1000); + milliseconds %= 1000; break; case 'millisecond': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); - ({ quotient: milliseconds, remainder: microseconds } = microseconds.divmod(1000)); + microseconds = MathTrunc(nanoseconds / 1000); + nanoseconds %= 1000; + milliseconds = MathTrunc(microseconds / 1000) + seconds * 1000; + microseconds %= 1000; + seconds = 0; break; case 'microsecond': - ({ quotient: microseconds, remainder: nanoseconds } = nanoseconds.divmod(1000)); + microseconds = MathTrunc(nanoseconds / 1000) + seconds * 1e6; + nanoseconds %= 1000; + seconds = 0; break; case 'nanosecond': + nanoseconds += seconds * 1e9; + seconds = 0; break; default: throw new Error('assert not reached'); } - days = days.toJSNumber() * sign; - hours = hours.toJSNumber() * sign; - minutes = minutes.toJSNumber() * sign; - seconds = seconds.toJSNumber() * sign; - milliseconds = milliseconds.toJSNumber() * sign; - microseconds = microseconds.toJSNumber() * sign; - nanoseconds = nanoseconds.toJSNumber() * sign; + days *= sign; + hours *= sign; + minutes *= sign; + seconds *= sign; + milliseconds *= sign; + microseconds *= sign; + nanoseconds *= sign; RejectDuration(0, 0, 0, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; @@ -3433,12 +3412,7 @@ export function BalanceTimeDuration( export function BalanceTimeDurationRelative( days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, largestUnit, zonedRelativeTo, timeZoneRec, @@ -3459,31 +3433,21 @@ export function BalanceTimeDurationRelative( ).epochNs; } - const endNs = AddInstant(intermediateNs, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); - nanoseconds = endNs.subtract(startNs); - if (nanoseconds.isZero()) { + const endNs = AddInstant(intermediateNs, norm); + norm = TimeDuration.fromEpochNsDiff(endNs, startNs); + if (norm.isZero()) { return { days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0, microseconds: 0, nanoseconds: 0 }; } if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || largestUnit === 'day') { precalculatedPlainDateTime ??= GetPlainDateTimeFor(timeZoneRec, startInstant, 'iso8601'); - ({ days, nanoseconds } = NanosecondsToDays(nanoseconds, zonedRelativeTo, timeZoneRec, precalculatedPlainDateTime)); + ({ days, norm } = NormalizedTimeDurationToDays(norm, zonedRelativeTo, timeZoneRec, precalculatedPlainDateTime)); largestUnit = 'hour'; } else { days = 0; } - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - 0, - 0, - 0, - 0, - 0, - 0, - nanoseconds, - largestUnit - )); - + const { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration(norm, largestUnit); return { days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; } @@ -3792,71 +3756,24 @@ export function DifferenceISODate(y1, m1, d1, y2, m2, d2, largestUnit = 'days') } export function DifferenceTime(h1, min1, s1, ms1, µs1, ns1, h2, min2, s2, ms2, µs2, ns2) { - let hours = h2 - h1; - let minutes = min2 - min1; - let seconds = s2 - s1; - let milliseconds = ms2 - ms1; - let microseconds = µs2 - µs1; - let nanoseconds = ns2 - ns1; - - const sign = DurationSign(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); - hours *= sign; - minutes *= sign; - seconds *= sign; - milliseconds *= sign; - microseconds *= sign; - nanoseconds *= sign; + const hours = h2 - h1; + const minutes = min2 - min1; + const seconds = s2 - s1; + const milliseconds = ms2 - ms1; + const microseconds = µs2 - µs1; + const nanoseconds = ns2 - ns1; + const norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); - let deltaDays = 0; - ({ - deltaDays, - hour: hours, - minute: minutes, - second: seconds, - millisecond: milliseconds, - microsecond: microseconds, - nanosecond: nanoseconds - } = BalanceTime(hours, minutes, seconds, milliseconds, microseconds, nanoseconds)); - - if (deltaDays != 0) throw new Error('assertion failure in DifferenceTime: _bt_.[[Days]] should be 0'); - hours *= sign; - minutes *= sign; - seconds *= sign; - milliseconds *= sign; - microseconds *= sign; - nanoseconds *= sign; + if (norm.abs().sec >= 86400) throw new Error('assertion failure in DifferenceTime: _bt_.[[Days]] should be 0'); - return { hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; + return norm; } -export function DifferenceInstant(ns1, ns2, increment, smallestUnit, largestUnit, roundingMode) { - const diff = ns2.minus(ns1); - - let hours = 0; - let minutes = 0; - let nanoseconds = diff.mod(1e3).toJSNumber(); - let microseconds = diff.divide(1e3).mod(1e3).toJSNumber(); - let milliseconds = diff.divide(1e6).mod(1e3).toJSNumber(); - let seconds = diff.divide(1e9).toJSNumber(); +export function DifferenceInstant(ns1, ns2, increment, smallestUnit, roundingMode) { + const diff = TimeDuration.fromEpochNsDiff(ns2, ns1); + if (smallestUnit === 'nanosecond' && increment === 1) return diff; - 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); + return RoundDuration(0, 0, 0, 0, diff, increment, smallestUnit, roundingMode).norm; } export function DifferenceDate(calendarRec, plainDate1, plainDate2, options) { @@ -3899,35 +3816,13 @@ export function DifferenceISODateTime( ) { // dateUntil must be looked up if date parts are not identical and largestUnit // is greater than 'day' - let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceTime( - h1, - min1, - s1, - ms1, - µs1, - ns1, - h2, - min2, - s2, - ms2, - µs2, - ns2 - ); + let timeDuration = DifferenceTime(h1, min1, s1, ms1, µs1, ns1, h2, min2, s2, ms2, µs2, ns2); - const timeSign = DurationSign(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + const timeSign = timeDuration.sign(); const dateSign = CompareISODate(y2, mon2, d2, y1, mon1, d1); if (dateSign === -timeSign) { ({ year: y1, month: mon1, day: d1 } = BalanceISODate(y1, mon1, d1 - timeSign)); - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - -timeSign, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - largestUnit - )); + timeDuration = timeDuration.add24HourDays(-timeSign); } const date1 = CreateTemporalDate(y1, mon1, d1, calendarRec.receiver); @@ -3939,19 +3834,8 @@ export function DifferenceISODateTime( const years = GetSlot(untilResult, YEARS); const months = GetSlot(untilResult, MONTHS); const weeks = GetSlot(untilResult, WEEKS); - let days = GetSlot(untilResult, DAYS); - // Signs of date part and time part may not agree; balance them together - ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - largestUnit - )); - return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; + const days = GetSlot(untilResult, DAYS); + return { years, months, weeks, days, norm: timeDuration }; } export function DifferenceZonedDateTime( @@ -3975,12 +3859,7 @@ export function DifferenceZonedDateTime( months: 0, weeks: 0, days: 0, - hours: 0, - minutes: 0, - seconds: 0, - milliseconds: 0, - microseconds: 0, - nanoseconds: 0 + norm: TimeDuration.ZERO }; } @@ -3991,7 +3870,7 @@ export function DifferenceZonedDateTime( const dtStart = precalculatedDtStart ?? GetPlainDateTimeFor(timeZoneRec, start, calendarRec.receiver); const dtEnd = GetPlainDateTimeFor(timeZoneRec, end, calendarRec.receiver); - let { years, months, weeks, days } = DifferenceISODateTime( + let { years, months, weeks } = DifferenceISODateTime( GetSlot(dtStart, ISO_YEAR), GetSlot(dtStart, ISO_MONTH), GetSlot(dtStart, ISO_DAY), @@ -4022,31 +3901,17 @@ export function DifferenceZonedDateTime( months, weeks, 0, - 0, - 0, - 0, - 0, - 0, - 0, + TimeDuration.ZERO, dtStart ); // may disambiguate - let timeRemainderNs = ns2.subtract(intermediateNs); + + let norm = TimeDuration.fromEpochNsDiff(ns2, intermediateNs); const intermediate = CreateTemporalZonedDateTime(intermediateNs, timeZoneRec.receiver, calendarRec.receiver); - ({ nanoseconds: timeRemainderNs, days } = NanosecondsToDays(timeRemainderNs, intermediate, timeZoneRec)); + let days; + ({ norm, days } = NormalizedTimeDurationToDays(norm, intermediate, timeZoneRec)); - // Finally, merge the date and time durations and return the merged result. - let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - 0, - 0, - 0, - 0, - 0, - 0, - timeRemainderNs, - 'hour' - ); - return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; + return { years, months, weeks, days, norm }; } export function GetDifferenceSettings(op, options, group, disallowed, fallbackSmallest, smallestLargestDefaultUnit) { @@ -4103,14 +3968,17 @@ export function DifferenceTemporalInstant(operation, instant, other, options) { const onens = GetSlot(instant, EPOCHNANOSECONDS); const twons = GetSlot(other, EPOCHNANOSECONDS); - let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceInstant( + const norm = DifferenceInstant( onens, twons, settings.roundingIncrement, settings.smallestUnit, - settings.largestUnit, settings.roundingMode ); + const { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( + norm, + settings.largestUnit + ); const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration( 0, @@ -4180,12 +4048,7 @@ export function DifferenceTemporalPlainDate(operation, plainDate, other, options months, weeks, days, - 0, - 0, - 0, - 0, - 0, - 0, + TimeDuration.ZERO, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, @@ -4255,44 +4118,39 @@ export function DifferenceTemporalPlainDateTime(operation, plainDateTime, other, calendarRec.lookup('dateUntil'); } - let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - DifferenceISODateTime( - GetSlot(plainDateTime, ISO_YEAR), - GetSlot(plainDateTime, ISO_MONTH), - GetSlot(plainDateTime, ISO_DAY), - GetSlot(plainDateTime, ISO_HOUR), - GetSlot(plainDateTime, ISO_MINUTE), - GetSlot(plainDateTime, ISO_SECOND), - GetSlot(plainDateTime, ISO_MILLISECOND), - GetSlot(plainDateTime, ISO_MICROSECOND), - GetSlot(plainDateTime, ISO_NANOSECOND), - GetSlot(other, ISO_YEAR), - GetSlot(other, ISO_MONTH), - GetSlot(other, ISO_DAY), - GetSlot(other, ISO_HOUR), - GetSlot(other, ISO_MINUTE), - GetSlot(other, ISO_SECOND), - GetSlot(other, ISO_MILLISECOND), - GetSlot(other, ISO_MICROSECOND), - GetSlot(other, ISO_NANOSECOND), - calendarRec, - settings.largestUnit, - resolvedOptions - ); + let { years, months, weeks, days, norm } = DifferenceISODateTime( + GetSlot(plainDateTime, ISO_YEAR), + GetSlot(plainDateTime, ISO_MONTH), + GetSlot(plainDateTime, ISO_DAY), + GetSlot(plainDateTime, ISO_HOUR), + GetSlot(plainDateTime, ISO_MINUTE), + GetSlot(plainDateTime, ISO_SECOND), + GetSlot(plainDateTime, ISO_MILLISECOND), + GetSlot(plainDateTime, ISO_MICROSECOND), + GetSlot(plainDateTime, ISO_NANOSECOND), + GetSlot(other, ISO_YEAR), + GetSlot(other, ISO_MONTH), + GetSlot(other, ISO_DAY), + GetSlot(other, ISO_HOUR), + GetSlot(other, ISO_MINUTE), + GetSlot(other, ISO_SECOND), + GetSlot(other, ISO_MILLISECOND), + GetSlot(other, ISO_MICROSECOND), + GetSlot(other, ISO_NANOSECOND), + calendarRec, + settings.largestUnit, + resolvedOptions + ); + let hours, minutes, seconds, milliseconds, microseconds, nanoseconds; if (!roundingIsNoop) { const relativeTo = TemporalDateTimeToDate(plainDateTime); - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + ({ years, months, weeks, days, norm } = RoundDuration( years, months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, @@ -4300,13 +4158,7 @@ export function DifferenceTemporalPlainDateTime(operation, plainDateTime, other, calendarRec )); ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm.add24HourDays(days), settings.largestUnit )); ({ years, months, weeks, days } = BalanceDateDurationRelative( @@ -4318,6 +4170,11 @@ export function DifferenceTemporalPlainDateTime(operation, plainDateTime, other, relativeTo, calendarRec )); + } else { + ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( + norm.add24HourDays(days), + settings.largestUnit + )); } return new Duration( @@ -4341,7 +4198,7 @@ export function DifferenceTemporalPlainTime(operation, plainTime, other, options const resolvedOptions = SnapshotOwnProperties(GetOptionsObject(options), null); const settings = GetDifferenceSettings(operation, resolvedOptions, 'time', [], 'nanosecond', 'hour'); - let { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceTime( + let norm = DifferenceTime( GetSlot(plainTime, ISO_HOUR), GetSlot(plainTime, ISO_MINUTE), GetSlot(plainTime, ISO_SECOND), @@ -4356,32 +4213,21 @@ export function DifferenceTemporalPlainTime(operation, plainTime, other, options GetSlot(other, ISO_NANOSECOND) ); if (settings.smallestUnit !== 'nanosecond' || settings.roundingIncrement !== 1) { - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + ({ norm } = RoundDuration( 0, 0, 0, 0, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode )); } - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - 0, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + const { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( + norm, settings.largestUnit - )); + ); const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration( 0, @@ -4442,12 +4288,7 @@ export function DifferenceTemporalPlainYearMonth(operation, yearMonth, other, op months, 0, 0, - 0, - 0, - 0, - 0, - 0, - 0, + TimeDuration.ZERO, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, @@ -4487,13 +4328,10 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, months = 0; weeks = 0; days = 0; - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceInstant( - ns1, - ns2, - settings.roundingIncrement, - settings.smallestUnit, - settings.largestUnit, - settings.roundingMode + const norm = DifferenceInstant(ns1, ns2, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode); + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( + norm, + settings.largestUnit )); } else { const timeZone = GetSlot(zonedDateTime, TIME_ZONE); @@ -4520,30 +4358,25 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, const plainRelativeTo = TemporalDateTimeToDate(precalculatedPlainDateTime); resolvedOptions.largestUnit = settings.largestUnit; - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - DifferenceZonedDateTime( - ns1, - ns2, - timeZoneRec, - calendarRec, - settings.largestUnit, - resolvedOptions, - precalculatedPlainDateTime - )); + let norm; + ({ years, months, weeks, days, norm } = DifferenceZonedDateTime( + ns1, + ns2, + timeZoneRec, + calendarRec, + settings.largestUnit, + resolvedOptions, + precalculatedPlainDateTime + )); const roundingIsNoop = settings.smallestUnit === 'nanosecond' && settings.roundingIncrement === 1; if (!roundingIsNoop) { - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( + ({ years, months, weeks, days, norm } = RoundDuration( years, months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, @@ -4553,18 +4386,16 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, timeZoneRec, precalculatedPlainDateTime )); + let deltaDays; + ({ days: deltaDays, norm } = NormalizedTimeDurationToDays(norm, zonedDateTime, timeZoneRec)); + days += deltaDays; ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = AdjustRoundedDurationDays( years, months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, settings.roundingIncrement, settings.smallestUnit, settings.roundingMode, @@ -4584,6 +4415,7 @@ export function DifferenceTemporalZonedDateTime(operation, zonedDateTime, other, calendarRec )); } + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration(norm, 'hour')); } return new Duration( @@ -4625,50 +4457,23 @@ export function AddDate(calendarRec, plainDate, duration, options = undefined) { let month = GetSlot(plainDate, ISO_MONTH); let day = GetSlot(plainDate, ISO_DAY); const overflow = ToTemporalOverflow(options); - const { days } = BalanceTimeDuration( - GetSlot(duration, DAYS), + const norm = TimeDuration.normalize( GetSlot(duration, HOURS), GetSlot(duration, MINUTES), GetSlot(duration, SECONDS), GetSlot(duration, MILLISECONDS), GetSlot(duration, MICROSECONDS), - GetSlot(duration, NANOSECONDS), - 'day' + GetSlot(duration, NANOSECONDS) ); + const days = GetSlot(duration, DAYS) + BalanceTimeDuration(norm, 'day').days; ({ year, month, day } = AddISODate(year, month, day, 0, 0, 0, days, overflow)); return CreateTemporalDate(year, month, day, calendarRec.receiver); } -export function AddTime( - hour, - minute, - second, - millisecond, - microsecond, - nanosecond, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds -) { - hour += hours; - minute += minutes; - second += seconds; - millisecond += milliseconds; - microsecond += microseconds; - nanosecond += nanoseconds; - let deltaDays = 0; - ({ deltaDays, hour, minute, second, millisecond, microsecond, nanosecond } = BalanceTime( - hour, - minute, - second, - millisecond, - microsecond, - nanosecond - )); - return { deltaDays, hour, minute, second, millisecond, microsecond, nanosecond }; +export function AddTime(hour, minute, second, millisecond, microsecond, nanosecond, norm) { + second += norm.sec; + nanosecond += norm.subsec; + return BalanceTime(hour, minute, second, millisecond, microsecond, nanosecond); } export function AddDuration( @@ -4711,14 +4516,10 @@ export function AddDuration( throw new RangeError('relativeTo is required for years, months, or weeks arithmetic'); } years = months = weeks = 0; + const norm1 = TimeDuration.normalize(h1, min1, s1, ms1, µs1, ns1); + const norm2 = TimeDuration.normalize(h2, min2, s2, ms2, µs2, ns2); ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - d1 + d2, - bigInt(h1).add(h2), - bigInt(min1).add(min2), - bigInt(s1).add(s2), - bigInt(ms1).add(ms2), - bigInt(µs1).add(µs2), - bigInt(ns1).add(ns2), + norm1.add(norm2).add24HourDays(d1 + d2), largestUnit )); } else if (plainRelativeTo) { @@ -4738,14 +4539,10 @@ export function AddDuration( weeks = GetSlot(untilResult, WEEKS); days = GetSlot(untilResult, DAYS); // Signs of date part and time part may not agree; balance them together + const norm1 = TimeDuration.normalize(h1, min1, s1, ms1, µs1, ns1); + const norm2 = TimeDuration.normalize(h2, min2, s2, ms2, µs2, ns2); ({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - days, - bigInt(h1).add(h2), - bigInt(min1).add(min2), - bigInt(s1).add(s2), - bigInt(ms1).add(ms2), - bigInt(µs1).add(µs2), - bigInt(ns1).add(ns2), + norm1.add(norm2).add24HourDays(days), largestUnit )); } else { @@ -4757,6 +4554,8 @@ export function AddDuration( if (largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week' || largestUnit === 'day') { startDateTime ??= GetPlainDateTimeFor(timeZoneRec, startInstant, calendar); } + const norm1 = TimeDuration.normalize(h1, min1, s1, ms1, µs1, ns1); + const norm2 = TimeDuration.normalize(h2, min2, s2, ms2, µs2, ns2); const intermediateNs = AddZonedDateTime( startInstant, timeZoneRec, @@ -4765,12 +4564,7 @@ export function AddDuration( mon1, w1, d1, - h1, - min1, - s1, - ms1, - µs1, - ns1, + norm1, startDateTime ); const endNs = AddZonedDateTime( @@ -4781,12 +4575,7 @@ export function AddDuration( mon2, w2, d2, - h2, - min2, - s2, - ms2, - µs2, - ns2 + norm2 ); if (largestUnit !== 'year' && largestUnit !== 'month' && largestUnit !== 'week' && largestUnit !== 'day') { // The user is only asking for a time difference, so return difference of instants. @@ -4794,42 +4583,28 @@ export function AddDuration( months = 0; weeks = 0; days = 0; - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = DifferenceInstant( + const norm = TimeDuration.fromEpochNsDiff(endNs, GetSlot(zonedRelativeTo, EPOCHNANOSECONDS)); + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration(norm, largestUnit)); + } else { + let norm; + ({ years, months, weeks, days, norm } = DifferenceZonedDateTime( GetSlot(zonedRelativeTo, EPOCHNANOSECONDS), endNs, - 1, - 'nanosecond', + timeZoneRec, + calendarRec, largestUnit, - 'halfExpand' + ObjectCreate(null), + startDateTime )); - } else { - ({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = - DifferenceZonedDateTime( - GetSlot(zonedRelativeTo, EPOCHNANOSECONDS), - endNs, - timeZoneRec, - calendarRec, - largestUnit, - ObjectCreate(null), - startDateTime - )); + ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration(norm, 'hour')); } } - RejectDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; } -export function AddInstant(epochNanoseconds, h, min, s, ms, µs, ns) { - let sum = bigInt.zero; - sum = sum.plus(bigInt(ns)); - sum = sum.plus(bigInt(µs).multiply(1e3)); - sum = sum.plus(bigInt(ms).multiply(1e6)); - sum = sum.plus(bigInt(s).multiply(1e9)); - sum = sum.plus(bigInt(min).multiply(60 * 1e9)); - sum = sum.plus(bigInt(h).multiply(60 * 60 * 1e9)); - - const result = bigInt(epochNanoseconds).plus(sum); +export function AddInstant(epochNanoseconds, norm) { + const result = norm.addToEpochNs(epochNanoseconds); ValidateEpochNanoseconds(result); return result; } @@ -4849,12 +4624,7 @@ export function AddDateTime( months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, options ) { // dateAdd must be looked up if years, months, weeks != 0 @@ -4867,12 +4637,7 @@ export function AddDateTime( millisecond, microsecond, nanosecond, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds + norm )); days += deltaDays; @@ -4903,12 +4668,7 @@ export function AddZonedDateTime( months, weeks, days, - h, - min, - s, - ms, - µs, - ns, + norm, precalculatedPlainDateTime = undefined, options = undefined ) { @@ -4930,14 +4690,14 @@ export function AddZonedDateTime( // BTW, this behavior is similar in spirit to offset: 'prefer' in `with`. const TemporalDuration = GetIntrinsic('%Temporal.Duration%'); if (DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0) === 0) { - return AddInstant(GetSlot(instant, EPOCHNANOSECONDS), h, min, s, ms, µs, ns); + return AddInstant(GetSlot(instant, EPOCHNANOSECONDS), norm); } const dt = precalculatedPlainDateTime ?? GetPlainDateTimeFor(timeZoneRec, instant, calendarRec.receiver); if (DurationSign(years, months, weeks, 0, 0, 0, 0, 0, 0, 0) === 0) { const overflow = ToTemporalOverflow(options); const intermediate = AddDaysToZonedDateTime(instant, dt, timeZoneRec, calendarRec.receiver, days, overflow).epochNs; - return AddInstant(intermediate, h, min, s, ms, µs, ns); + return AddInstant(intermediate, norm); } // RFC 5545 requires the date portion to be added in calendar days and the @@ -4966,7 +4726,7 @@ export function AddZonedDateTime( // Note that 'compatible' is used below because this disambiguation behavior // is required by RFC 5545. const instantIntermediate = GetInstantFor(timeZoneRec, dtIntermediate, 'compatible'); - return AddInstant(GetSlot(instantIntermediate, EPOCHNANOSECONDS), h, min, s, ms, µs, ns); + return AddInstant(GetSlot(instantIntermediate, EPOCHNANOSECONDS), norm); } export function AddDaysToZonedDateTime(instant, dateTime, timeZoneRec, calendar, days, overflow = 'constrain') { @@ -5086,8 +4846,7 @@ export function AddDurationToOrSubtractDurationFromInstant(operation, instant, d 'weeks', 'days' ]); - const ns = AddInstant( - GetSlot(instant, EPOCHNANOSECONDS), + const norm = TimeDuration.normalize( sign * hours, sign * minutes, sign * seconds, @@ -5095,6 +4854,7 @@ export function AddDurationToOrSubtractDurationFromInstant(operation, instant, d sign * microseconds, sign * nanoseconds ); + const ns = AddInstant(GetSlot(instant, EPOCHNANOSECONDS), norm); const Instant = GetIntrinsic('%Temporal.Instant%'); return new Instant(ns); } @@ -5110,6 +4870,14 @@ export function AddDurationToOrSubtractDurationFromPlainDateTime(operation, date calendarRec.lookup('dateAdd'); } + const norm = TimeDuration.normalize( + sign * hours, + sign * minutes, + sign * seconds, + sign * milliseconds, + sign * microseconds, + sign * nanoseconds + ); const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = AddDateTime( GetSlot(dateTime, ISO_YEAR), GetSlot(dateTime, ISO_MONTH), @@ -5125,12 +4893,7 @@ export function AddDurationToOrSubtractDurationFromPlainDateTime(operation, date sign * months, sign * weeks, sign * days, - sign * hours, - sign * minutes, - sign * seconds, - sign * milliseconds, - sign * microseconds, - sign * nanoseconds, + norm, options ); return CreateTemporalDateTime( @@ -5150,6 +4913,14 @@ export function AddDurationToOrSubtractDurationFromPlainDateTime(operation, date export function AddDurationToOrSubtractDurationFromPlainTime(operation, temporalTime, durationLike) { const sign = operation === 'subtract' ? -1 : 1; const { hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ToTemporalDurationRecord(durationLike); + const norm = TimeDuration.normalize( + sign * hours, + sign * minutes, + sign * seconds, + sign * milliseconds, + sign * microseconds, + sign * nanoseconds + ); let { hour, minute, second, millisecond, microsecond, nanosecond } = AddTime( GetSlot(temporalTime, ISO_HOUR), GetSlot(temporalTime, ISO_MINUTE), @@ -5157,12 +4928,7 @@ export function AddDurationToOrSubtractDurationFromPlainTime(operation, temporal GetSlot(temporalTime, ISO_MILLISECOND), GetSlot(temporalTime, ISO_MICROSECOND), GetSlot(temporalTime, ISO_NANOSECOND), - sign * hours, - sign * minutes, - sign * seconds, - sign * milliseconds, - sign * microseconds, - sign * nanoseconds + norm ); ({ hour, minute, second, millisecond, microsecond, nanosecond } = RegulateTime( hour, @@ -5194,7 +4960,8 @@ export function AddDurationToOrSubtractDurationFromPlainYearMonth(operation, yea }; } let { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = duration; - ({ days } = BalanceTimeDuration(days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'day')); + const norm = TimeDuration.normalize(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); + days += BalanceTimeDuration(norm, 'day').days; const sign = DurationSign(years, months, weeks, days, 0, 0, 0, 0, 0, 0); const calendarRec = new CalendarMethodRecord(GetSlot(yearMonth, CALENDAR)); @@ -5255,6 +5022,14 @@ export function AddDurationToOrSubtractDurationFromZonedDateTime(operation, zone if (years !== 0 || months !== 0 || weeks !== 0) { calendarRec.lookup('dateAdd'); } + const norm = TimeDuration.normalize( + sign * hours, + sign * minutes, + sign * seconds, + sign * milliseconds, + sign * microseconds, + sign * nanoseconds + ); const epochNanoseconds = AddZonedDateTime( GetSlot(zonedDateTime, INSTANT), timeZoneRec, @@ -5263,12 +5038,7 @@ export function AddDurationToOrSubtractDurationFromZonedDateTime(operation, zone sign * months, sign * weeks, sign * days, - sign * hours, - sign * minutes, - sign * seconds, - sign * milliseconds, - sign * microseconds, - sign * nanoseconds, + norm, undefined, options ); @@ -5450,12 +5220,7 @@ export function MoveRelativeZonedDateTime( months, weeks, days, - 0, - 0, - 0, - 0, - 0, - 0, + TimeDuration.ZERO, precalculatedPlainDateTime ); return CreateTemporalZonedDateTime(intermediateNs, timeZoneRec.receiver, calendarRec.receiver); @@ -5466,12 +5231,7 @@ export function AdjustRoundedDurationDays( months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, increment, unit, roundingMode, @@ -5489,7 +5249,7 @@ export function AdjustRoundedDurationDays( unit === 'day' || (unit === 'nanosecond' && increment === 1) ) { - return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; + return { years, months, weeks, days, norm }; } // There's one more round of rounding possible: if relativeTo is a @@ -5500,8 +5260,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(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); - const direction = MathSign(timeRemainderNs.toJSNumber()); + const direction = norm.sign(); const calendar = GetSlot(zonedRelativeTo, CALENDAR); // requires dateAdd if years...weeks != 0 @@ -5513,22 +5272,17 @@ export function AdjustRoundedDurationDays( months, weeks, days, - 0, - 0, - 0, - 0, - 0, - 0, + TimeDuration.ZERO, precalculatedPlainDateTime ); const TemporalInstant = GetIntrinsic('%Temporal.Instant%'); const dayStartInstant = new TemporalInstant(dayStart); const dayStartDateTime = GetPlainDateTimeFor(timeZoneRec, dayStartInstant, calendar); const dayEnd = AddDaysToZonedDateTime(dayStartInstant, dayStartDateTime, timeZoneRec, calendar, direction).epochNs; - const dayLengthNs = dayEnd.subtract(dayStart); + const dayLength = TimeDuration.fromEpochNsDiff(dayEnd, dayStart); - const oneDayLess = timeRemainderNs.subtract(dayLengthNs); - if (oneDayLess.multiply(direction).geq(0)) { + const oneDayLess = norm.subtract(dayLength); + if (oneDayLess.sign() * direction >= 0) { // requires dateAdd and dateUntil if years...weeks != 0 ({ years, months, weeks, days } = AddDuration( years, @@ -5557,34 +5311,9 @@ export function AdjustRoundedDurationDays( timeZoneRec, precalculatedPlainDateTime )); - // no calendar calls - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = RoundDuration( - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - oneDayLess.toJSNumber(), - increment, - unit, - roundingMode - )); - ({ hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = BalanceTimeDuration( - 0, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, - 'hour' - )); + ({ norm } = RoundDuration(0, 0, 0, 0, oneDayLess, increment, unit, roundingMode)); } - return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }; + return { years, months, weeks, days, norm }; } export function RoundDuration( @@ -5592,12 +5321,7 @@ export function RoundDuration( months, weeks, days, - hours, - minutes, - seconds, - milliseconds, - microseconds, - nanoseconds, + norm, increment, unit, roundingMode, @@ -5621,7 +5345,6 @@ 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(hours, minutes, seconds, milliseconds, microseconds, nanoseconds); let deltaDays; if (zonedRelativeTo) { const intermediate = MoveRelativeZonedDateTime( @@ -5634,14 +5357,12 @@ export function RoundDuration( days, precalculatedPlainDateTime ); - ({ days: deltaDays, nanoseconds, dayLengthNs } = NanosecondsToDays(nanoseconds, intermediate, timeZoneRec)); + ({ days: deltaDays, norm, dayLengthNs } = NormalizedTimeDurationToDays(norm, intermediate, timeZoneRec)); } else { - ({ quotient: deltaDays, remainder: nanoseconds } = nanoseconds.divmod(DAY_NANOS)); - deltaDays = deltaDays.toJSNumber(); - dayLengthNs = DAY_NANOS.toJSNumber(); + ({ quotient: deltaDays, remainder: norm } = norm.divmod(DAY_NANOS)); + dayLengthNs = DAY_NANOS; } days += deltaDays; - hours = minutes = seconds = milliseconds = microseconds = 0; } let total; @@ -5692,12 +5413,13 @@ export function RoundDuration( oneYearDays = MathAbs(oneYearDays); if (oneYearDays === 0) throw new RangeError('custom calendar reported that a year is 0 days long'); const divisor = bigInt(oneYearDays).multiply(dayLengthNs); - nanoseconds = divisor.multiply(years).plus(bigInt(days).multiply(dayLengthNs)).plus(nanoseconds); + const nanoseconds = divisor.multiply(years).plus(bigInt(days).multiply(dayLengthNs)).plus(norm.totalNs); const rounded = RoundNumberToIncrement(nanoseconds, divisor.multiply(increment).toJSNumber(), roundingMode); const { quotient, remainder } = nanoseconds.divmod(divisor); total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; years = rounded.divide(divisor).toJSNumber(); - nanoseconds = months = weeks = days = 0; + months = weeks = days = 0; + norm = TimeDuration.ZERO; break; } case 'month': { @@ -5740,12 +5462,13 @@ export function RoundDuration( oneMonthDays = MathAbs(oneMonthDays); if (oneMonthDays === 0) throw new RangeError('custom calendar reported that a month is 0 days long'); const divisor = bigInt(oneMonthDays).multiply(dayLengthNs); - nanoseconds = divisor.multiply(months).plus(bigInt(days).multiply(dayLengthNs)).plus(nanoseconds); + const nanoseconds = divisor.multiply(months).plus(bigInt(days).multiply(dayLengthNs)).plus(norm.totalNs); const rounded = RoundNumberToIncrement(nanoseconds, divisor.multiply(increment), roundingMode); const { quotient, remainder } = nanoseconds.divmod(divisor); total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; months = rounded.divide(divisor).toJSNumber(); - nanoseconds = weeks = days = 0; + weeks = days = 0; + norm = TimeDuration.ZERO; break; } case 'week': { @@ -5778,97 +5501,62 @@ export function RoundDuration( oneWeekDays = MathAbs(oneWeekDays); if (oneWeekDays === 0) throw new RangeError('custom calendar reported that a week is 0 days long'); const divisor = bigInt(oneWeekDays).multiply(dayLengthNs); - nanoseconds = divisor.multiply(weeks).plus(bigInt(days).multiply(dayLengthNs)).plus(nanoseconds); + const nanoseconds = divisor.multiply(weeks).plus(bigInt(days).multiply(dayLengthNs)).plus(norm.totalNs); const rounded = RoundNumberToIncrement(nanoseconds, divisor.multiply(increment), roundingMode); const { quotient, remainder } = nanoseconds.divmod(divisor); total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; weeks = rounded.divide(divisor).toJSNumber(); - nanoseconds = days = 0; + days = 0; + norm = TimeDuration.ZERO; break; } case 'day': { const divisor = bigInt(dayLengthNs); - nanoseconds = divisor.multiply(days).plus(nanoseconds); + const nanoseconds = divisor.multiply(days).plus(norm.totalNs); const rounded = RoundNumberToIncrement(nanoseconds, divisor.multiply(increment), roundingMode); const { quotient, remainder } = nanoseconds.divmod(divisor); total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; days = rounded.divide(divisor).toJSNumber(); - nanoseconds = 0; + norm = TimeDuration.ZERO; break; } case 'hour': { const divisor = 3600e9; - nanoseconds = bigInt(hours) - .multiply(3600e9) - .plus(bigInt(minutes).multiply(60e9)) - .plus(bigInt(seconds).multiply(1e9)) - .plus(bigInt(milliseconds).multiply(1e6)) - .plus(bigInt(microseconds).multiply(1e3)) - .plus(nanoseconds); - const { quotient, remainder } = nanoseconds.divmod(divisor); - total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; - const rounded = RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); - hours = rounded.divide(divisor).toJSNumber(); - minutes = seconds = milliseconds = microseconds = nanoseconds = 0; + total = norm.fdiv(divisor); + norm = norm.round(divisor * increment, roundingMode); break; } case 'minute': { const divisor = 60e9; - nanoseconds = bigInt(minutes) - .multiply(60e9) - .plus(bigInt(seconds).multiply(1e9)) - .plus(bigInt(milliseconds).multiply(1e6)) - .plus(bigInt(microseconds).multiply(1e3)) - .plus(nanoseconds); - const { quotient, remainder } = nanoseconds.divmod(divisor); - total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; - const rounded = RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); - minutes = rounded.divide(divisor).toJSNumber(); - seconds = milliseconds = microseconds = nanoseconds = 0; + total = norm.fdiv(divisor); + norm = norm.round(divisor * increment, roundingMode); break; } case 'second': { const divisor = 1e9; - nanoseconds = bigInt(seconds) - .multiply(1e9) - .plus(bigInt(milliseconds).multiply(1e6)) - .plus(bigInt(microseconds).multiply(1e3)) - .plus(nanoseconds); - const { quotient, remainder } = nanoseconds.divmod(divisor); - total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; - const rounded = RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); - seconds = rounded.divide(divisor).toJSNumber(); - milliseconds = microseconds = nanoseconds = 0; + total = norm.fdiv(divisor); + norm = norm.round(divisor * increment, roundingMode); break; } case 'millisecond': { const divisor = 1e6; - nanoseconds = bigInt(milliseconds).multiply(1e6).plus(bigInt(microseconds).multiply(1e3)).plus(nanoseconds); - const { quotient, remainder } = nanoseconds.divmod(divisor); - total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; - const rounded = RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); - milliseconds = rounded.divide(divisor).toJSNumber(); - microseconds = nanoseconds = 0; + total = norm.fdiv(divisor); + norm = norm.round(divisor * increment, roundingMode); break; } case 'microsecond': { const divisor = 1e3; - nanoseconds = bigInt(microseconds).multiply(1e3).plus(nanoseconds); - const { quotient, remainder } = nanoseconds.divmod(divisor); - total = quotient.toJSNumber() + remainder.toJSNumber() / divisor; - const rounded = RoundNumberToIncrement(nanoseconds, divisor * increment, roundingMode); - microseconds = rounded.divide(divisor).toJSNumber(); - nanoseconds = 0; + total = norm.fdiv(divisor); + norm = norm.round(divisor * increment, roundingMode); break; } case 'nanosecond': { - total = nanoseconds; - nanoseconds = RoundNumberToIncrement(bigInt(nanoseconds), increment, roundingMode).toJSNumber(); + total = norm.totalNs.toJSNumber(); + norm = norm.round(increment, roundingMode); break; } } - RejectDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); - return { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, total }; + return { years, months, weeks, days, norm, total }; } export function CompareISODate(y1, m1, d1, y2, m2, d2) { diff --git a/polyfill/lib/timeduration.mjs b/polyfill/lib/timeduration.mjs new file mode 100644 index 0000000000..5fe073a9b6 --- /dev/null +++ b/polyfill/lib/timeduration.mjs @@ -0,0 +1,135 @@ +import bigInt from 'big-integer'; + +const MathAbs = Math.abs; +const NumberIsInteger = Number.isInteger; +const NumberIsSafeInteger = Number.isSafeInteger; + +export class TimeDuration { + static MAX = bigInt('9007199254740991999999999'); + static ZERO = new TimeDuration(bigInt.zero); + + constructor(totalNs) { + if (typeof totalNs === 'number') throw new Error('assertion failed: big integer required'); + this.totalNs = bigInt(totalNs); + if (this.totalNs.abs().greater(TimeDuration.MAX)) throw new Error('assertion failed: integer too big'); + + const { quotient, remainder } = this.totalNs.divmod(1e9); + this.sec = quotient.toJSNumber(); + this.subsec = remainder.toJSNumber(); + if (!NumberIsSafeInteger(this.sec)) throw new Error('assertion failed: seconds too big'); + if (MathAbs(this.subsec) > 999_999_999) throw new Error('assertion failed: subseconds too big'); + } + + static #validateNew(totalNs, operation) { + if (totalNs.abs().greater(TimeDuration.MAX)) { + throw new RangeError(`${operation} of duration time units cannot exceed ${TimeDuration.MAX} s`); + } + return new TimeDuration(totalNs); + } + + static fromEpochNsDiff(epochNs1, epochNs2) { + const diff = bigInt(epochNs1).subtract(epochNs2); + // No extra validate step. Should instead fail assertion if too big + return new TimeDuration(diff); + } + + static normalize(h, min, s, ms, µs, ns) { + const totalNs = bigInt(ns) + .add(bigInt(µs).multiply(1e3)) + .add(bigInt(ms).multiply(1e6)) + .add(bigInt(s).multiply(1e9)) + .add(bigInt(min).multiply(60e9)) + .add(bigInt(h).multiply(3600e9)); + return TimeDuration.#validateNew(totalNs, 'total'); + } + + abs() { + return new TimeDuration(this.totalNs.abs()); + } + + add(other) { + return TimeDuration.#validateNew(this.totalNs.add(other.totalNs), 'sum'); + } + + add24HourDays(days) { + if (!NumberIsInteger(days)) throw new Error('assertion failed: days is an integer'); + return TimeDuration.#validateNew(this.totalNs.add(bigInt(days).multiply(86400e9)), 'sum'); + } + + addToEpochNs(epochNs) { + return bigInt(epochNs).add(this.totalNs); + } + + cmp(other) { + return this.totalNs.compare(other.totalNs); + } + + divmod(n) { + if (n === 0) throw new Error('division by zero'); + const { quotient, remainder } = this.totalNs.divmod(n); + const q = quotient.toJSNumber(); + if (!NumberIsSafeInteger(q)) throw new Error('assertion failed: quotient too big'); + const r = new TimeDuration(remainder); + return { quotient: q, remainder: r }; + } + + fdiv(n) { + const { quotient, remainder } = this.divmod(n); + return quotient + remainder.totalNs.toJSNumber() / n; + } + + isZero() { + return this.totalNs.isZero(); + } + + round(increment, mode) { + if (increment === 1) return this; + let { quotient, remainder } = this.totalNs.divmod(increment); + if (remainder.equals(bigInt.zero)) return this; + const sign = remainder.lt(bigInt.zero) ? -1 : 1; + const tiebreaker = remainder.multiply(2).abs(); + const tie = tiebreaker.equals(increment); + const expandIsNearer = tiebreaker.gt(increment); + switch (mode) { + case 'ceil': + if (sign > 0) quotient = quotient.add(sign); + break; + case 'floor': + if (sign < 0) quotient = quotient.add(sign); + break; + case 'expand': + // always expand if there is a remainder + quotient = quotient.add(sign); + break; + case 'trunc': + // no change needed, because divmod is a truncation + break; + case 'halfCeil': + if (expandIsNearer || (tie && sign > 0)) quotient = quotient.add(sign); + break; + case 'halfFloor': + if (expandIsNearer || (tie && sign < 0)) quotient = quotient.add(sign); + break; + case 'halfExpand': + // "half up away from zero" + if (expandIsNearer || tie) quotient = quotient.add(sign); + break; + case 'halfTrunc': + if (expandIsNearer) quotient = quotient.add(sign); + break; + case 'halfEven': { + if (expandIsNearer || (tie && quotient.isOdd())) quotient = quotient.add(sign); + break; + } + } + return TimeDuration.#validateNew(quotient.multiply(increment), 'rounding'); + } + + sign() { + return this.cmp(new TimeDuration(0n)); + } + + subtract(other) { + return TimeDuration.#validateNew(this.totalNs.subtract(other.totalNs), 'difference'); + } +} diff --git a/polyfill/test/all.mjs b/polyfill/test/all.mjs index b339477795..5f0ddaf01d 100644 --- a/polyfill/test/all.mjs +++ b/polyfill/test/all.mjs @@ -14,6 +14,9 @@ import './datemath.mjs'; // tests of internals, not suitable for test262 import './ecmascript.mjs'; +// Internal 96-bit integer implementation, not suitable for test262 +import './timeduration.mjs'; + Promise.resolve() .then(() => { return Demitasse.report(Pretty.reporter); diff --git a/polyfill/test/timeduration.mjs b/polyfill/test/timeduration.mjs new file mode 100644 index 0000000000..babcd8db82 --- /dev/null +++ b/polyfill/test/timeduration.mjs @@ -0,0 +1,507 @@ +import Demitasse from '@pipobscure/demitasse'; +const { describe, it, report } = Demitasse; + +import Pretty from '@pipobscure/demitasse-pretty'; +const { reporter } = Pretty; + +import { strict as assert, AssertionError } from 'assert'; +const { equal, throws } = assert; + +import { TimeDuration } from '../lib/timeduration.mjs'; + +function check(timeDuration, sec, subsec) { + equal(timeDuration.sec, sec); + equal(timeDuration.subsec, subsec); +} + +function checkBigInt(value, bigint) { + if (value && typeof value === 'object') { + equal(value.value, bigint); // bigInteger wrapper + } else { + equal(value, bigint); // real bigint + } +} + +function checkFloat(value, float) { + if (Math.abs(value - float) > Number.EPSILON) { + throw new AssertionError({ + message: `Expected ${value} to be within ɛ of ${float}`, + expected: float, + actual: value, + operator: 'checkFloat' + }); + } +} + +describe('Normalized time duration', () => { + describe('construction', () => { + it('basic', () => { + check(new TimeDuration(123456789_987654321n), 123456789, 987654321); + check(new TimeDuration(-987654321_123456789n), -987654321, -123456789); + }); + + it('either sign with zero in the other component', () => { + check(new TimeDuration(123n), 0, 123); + check(new TimeDuration(-123n), 0, -123); + check(new TimeDuration(123_000_000_000n), 123, 0); + check(new TimeDuration(-123_000_000_000n), -123, 0); + }); + }); + + describe('construction impossible', () => { + it('out of range', () => { + throws(() => new TimeDuration(2n ** 53n * 1_000_000_000n)); + throws(() => new TimeDuration(-(2n ** 53n * 1_000_000_000n))); + }); + + it('not an integer', () => { + throws(() => new TimeDuration(Math.PI)); + }); + }); + + describe('fromEpochNsDiff()', () => { + it('basic', () => { + check(TimeDuration.fromEpochNsDiff(1695930183_043174412n, 1695930174_412168313n), 8, 631006099); + check(TimeDuration.fromEpochNsDiff(1695930174_412168313n, 1695930183_043174412n), -8, -631006099); + }); + + it('pre-epoch', () => { + check(TimeDuration.fromEpochNsDiff(-80000_987_654_321n, -86400_123_456_789n), 6399, 135802468); + check(TimeDuration.fromEpochNsDiff(-86400_123_456_789n, -80000_987_654_321n), -6399, -135802468); + }); + + it('cross-epoch', () => { + check(TimeDuration.fromEpochNsDiff(1_000_001_000n, -2_000_002_000n), 3, 3000); + check(TimeDuration.fromEpochNsDiff(-2_000_002_000n, 1_000_001_000n), -3, -3000); + }); + + it('maximum epoch difference', () => { + const max = 86400_0000_0000_000_000_000n; + check(TimeDuration.fromEpochNsDiff(max, -max), 172800_0000_0000, 0); + check(TimeDuration.fromEpochNsDiff(-max, max), -172800_0000_0000, 0); + }); + }); + + describe('normalize()', () => { + it('basic', () => { + check(TimeDuration.normalize(1, 1, 1, 1, 1, 1), 3661, 1001001); + check(TimeDuration.normalize(-1, -1, -1, -1, -1, -1), -3661, -1001001); + }); + + it('overflow from one unit to another', () => { + check(TimeDuration.normalize(1, 61, 61, 998, 1000, 1000), 7321, 999001000); + check(TimeDuration.normalize(-1, -61, -61, -998, -1000, -1000), -7321, -999001000); + }); + + it('overflow from subseconds to seconds', () => { + check(TimeDuration.normalize(0, 0, 1, 1000, 0, 0), 2, 0); + check(TimeDuration.normalize(0, 0, -1, -1000, 0, 0), -2, 0); + }); + + it('multiple overflows from subseconds to seconds', () => { + check(TimeDuration.normalize(0, 0, 0, 1234567890, 1234567890, 1234567890), 1235803, 692457890); + check(TimeDuration.normalize(0, 0, 0, -1234567890, -1234567890, -1234567890), -1235803, -692457890); + }); + + it('fails on overflow', () => { + throws(() => TimeDuration.normalize(2501999792984, 0, 0, 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(-2501999792984, 0, 0, 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 150119987579017, 0, 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, -150119987579017, 0, 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, 2 ** 53, 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, -(2 ** 53), 0, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, Number.MAX_SAFE_INTEGER, 1000, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, -Number.MAX_SAFE_INTEGER, -1000, 0, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, Number.MAX_SAFE_INTEGER, 0, 1000000, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, -Number.MAX_SAFE_INTEGER, 0, -1000000, 0), RangeError); + throws(() => TimeDuration.normalize(0, 0, Number.MAX_SAFE_INTEGER, 0, 0, 1000000000), RangeError); + throws(() => TimeDuration.normalize(0, 0, -Number.MAX_SAFE_INTEGER, 0, 0, -1000000000), RangeError); + }); + }); + + describe('abs()', () => { + it('positive', () => { + const d = new TimeDuration(123_456_654_321n); + check(d.abs(), 123, 456_654_321); + }); + + it('negative', () => { + const d = new TimeDuration(-123_456_654_321n); + check(d.abs(), 123, 456_654_321); + }); + + it('zero', () => { + const d = new TimeDuration(0n); + check(d.abs(), 0, 0); + }); + }); + + describe('add()', () => { + it('basic', () => { + const d1 = new TimeDuration(123_456_654_321_123_456n); + const d2 = new TimeDuration(654_321_123_456_654_321n); + check(d1.add(d2), 777_777_777, 777_777_777); + }); + + it('negative', () => { + const d1 = new TimeDuration(-123_456_654_321_123_456n); + const d2 = new TimeDuration(-654_321_123_456_654_321n); + check(d1.add(d2), -777_777_777, -777_777_777); + }); + + it('signs differ', () => { + const d1 = new TimeDuration(333_333_333_333_333_333n); + const d2 = new TimeDuration(-222_222_222_222_222_222n); + check(d1.add(d2), 111_111_111, 111_111_111); + + const d3 = new TimeDuration(-333_333_333_333_333_333n); + const d4 = new TimeDuration(222_222_222_222_222_222n); + check(d3.add(d4), -111_111_111, -111_111_111); + }); + + it('cross zero', () => { + const d1 = new TimeDuration(222_222_222_222_222_222n); + const d2 = new TimeDuration(-333_333_333_333_333_333n); + check(d1.add(d2), -111_111_111, -111_111_111); + }); + + it('overflow from subseconds to seconds', () => { + const d1 = new TimeDuration(999_999_999n); + const d2 = new TimeDuration(2n); + check(d1.add(d2), 1, 1); + }); + + it('fails on overflow', () => { + const d1 = new TimeDuration(2n ** 52n * 1_000_000_000n); + throws(() => d1.add(d1), RangeError); + }); + }); + + describe('add24HourDays()', () => { + it('basic', () => { + const d = new TimeDuration(111_111_111_111_111_111n); + check(d.add24HourDays(10), 111_975_111, 111_111_111); + }); + + it('negative', () => { + const d = new TimeDuration(-111_111_111_111_111_111n); + check(d.add24HourDays(-10), -111_975_111, -111_111_111); + }); + + it('signs differ', () => { + const d1 = new TimeDuration(864000_000_000_000n); + check(d1.add24HourDays(-5), 432000, 0); + + const d2 = new TimeDuration(-864000_000_000_000n); + check(d2.add24HourDays(5), -432000, 0); + }); + + it('cross zero', () => { + const d1 = new TimeDuration(86400_000_000_000n); + check(d1.add24HourDays(-2), -86400, 0); + + const d2 = new TimeDuration(-86400_000_000_000n); + check(d2.add24HourDays(3), 172800, 0); + }); + + it('overflow from subseconds to seconds', () => { + const d1 = new TimeDuration(-86400_333_333_333n); + check(d1.add24HourDays(2), 86399, 666_666_667); + + const d2 = new TimeDuration(86400_333_333_333n); + check(d2.add24HourDays(-2), -86399, -666_666_667); + }); + + it('does not accept non-integers', () => { + const d = new TimeDuration(0n); + throws(() => d.add24HourDays(1.5), Error); + }); + + it('fails on overflow', () => { + const d = new TimeDuration(0n); + throws(() => d.add24HourDays(104249991375), RangeError); + throws(() => d.add24HourDays(-104249991375), RangeError); + }); + }); + + describe('addToEpochNs()', () => { + it('basic', () => { + const d = new TimeDuration(123_456_654_321_123_456n); + checkBigInt(d.addToEpochNs(654_321_123_456_654_321n), 777_777_777_777_777_777n); + }); + + it('negative', () => { + const d = new TimeDuration(-123_456_654_321_123_456n); + checkBigInt(d.addToEpochNs(-654_321_123_456_654_321n), -777_777_777_777_777_777n); + }); + + it('signs differ', () => { + const d1 = new TimeDuration(333_333_333_333_333_333n); + checkBigInt(d1.addToEpochNs(-222_222_222_222_222_222n), 111_111_111_111_111_111n); + + const d2 = new TimeDuration(-333_333_333_333_333_333n); + checkBigInt(d2.addToEpochNs(222_222_222_222_222_222n), -111_111_111_111_111_111n); + }); + + it('cross zero', () => { + const d = new TimeDuration(222_222_222_222_222_222n); + checkBigInt(d.addToEpochNs(-333_333_333_333_333_333n), -111_111_111_111_111_111n); + }); + + it('does not fail on overflow, epochNs overflow is checked elsewhere', () => { + const d = new TimeDuration(86400_0000_0000_000_000_000n); + checkBigInt(d.addToEpochNs(86400_0000_0000_000_000_000n), 172800_0000_0000_000_000_000n); + }); + }); + + describe('cmp()', () => { + it('equal', () => { + const d1 = new TimeDuration(123_000_000_456n); + const d2 = new TimeDuration(123_000_000_456n); + equal(d1.cmp(d2), 0); + equal(d2.cmp(d1), 0); + }); + + it('unequal', () => { + const smaller = new TimeDuration(123_000_000_456n); + const larger = new TimeDuration(654_000_000_321n); + equal(smaller.cmp(larger), -1); + equal(larger.cmp(smaller), 1); + }); + + it('cross sign', () => { + const neg = new TimeDuration(-654_000_000_321n); + const pos = new TimeDuration(123_000_000_456n); + equal(neg.cmp(pos), -1); + equal(pos.cmp(neg), 1); + }); + }); + + describe('divmod()', () => { + it('divide by 1', () => { + const d = new TimeDuration(1_234_567_890_987n); + const { quotient, remainder } = d.divmod(1); + equal(quotient, 1234567890987); + check(remainder, 0, 0); + }); + + it('divide by self', () => { + const d = new TimeDuration(1_234_567_890n); + const { quotient, remainder } = d.divmod(1_234_567_890); + equal(quotient, 1); + check(remainder, 0, 0); + }); + + it('no remainder', () => { + const d = new TimeDuration(1_234_000_000n); + const { quotient, remainder } = d.divmod(1e6); + equal(quotient, 1234); + check(remainder, 0, 0); + }); + + it('divide by -1', () => { + const d = new TimeDuration(1_234_567_890_987n); + const { quotient, remainder } = d.divmod(-1); + equal(quotient, -1_234_567_890_987); + check(remainder, 0, 0); + }); + + it('zero seconds remainder has sign of dividend', () => { + const d1 = new TimeDuration(1_234_567_890n); + let { quotient, remainder } = d1.divmod(-1e6); + equal(quotient, -1234); + check(remainder, 0, 567890); + const d2 = new TimeDuration(-1_234_567_890n); + ({ quotient, remainder } = d2.divmod(1e6)); + equal(quotient, -1234); + check(remainder, 0, -567890); + }); + + it('nonzero seconds remainder has sign of dividend', () => { + const d1 = new TimeDuration(10_234_567_890n); + let { quotient, remainder } = d1.divmod(-9e9); + equal(quotient, -1); + check(remainder, 1, 234567890); + const d2 = new TimeDuration(-10_234_567_890n); + ({ quotient, remainder } = d2.divmod(9e9)); + equal(quotient, -1); + check(remainder, -1, -234567890); + }); + + it('negative with zero seconds remainder', () => { + const d = new TimeDuration(-1_234_567_890n); + const { quotient, remainder } = d.divmod(-1e6); + equal(quotient, 1234); + check(remainder, 0, -567890); + }); + + it('negative with nonzero seconds remainder', () => { + const d = new TimeDuration(-10_234_567_890n); + const { quotient, remainder } = d.divmod(-9e9); + equal(quotient, 1); + check(remainder, -1, -234567890); + }); + + it('quotient larger than seconds', () => { + const d = TimeDuration.normalize(25 + 5 * 24, 0, 86401, 333, 666, 999); + const { quotient, remainder } = d.divmod(86400e9); + equal(quotient, 7); + check(remainder, 3601, 333666999); + }); + + it('quotient smaller than seconds', () => { + const d = new TimeDuration(90061_333666999n); + const result1 = d.divmod(1000); + equal(result1.quotient, 90061333666); + check(result1.remainder, 0, 999); + + const result2 = d.divmod(10); + equal(result2.quotient, 9006133366699); + check(result2.remainder, 0, 9); + + const result3 = d.divmod(3); + equal(result3.quotient, 30020444555666); + check(result3.remainder, 0, 1); + }); + + it('divide by 0', () => { + const d = new TimeDuration(90061_333666999n); + throws(() => d.divmod(0), Error); + }); + }); + + describe('fdiv()', () => { + it('divide by 1', () => { + const d = new TimeDuration(1_234_567_890_987n); + equal(d.fdiv(1), 1_234_567_890_987); + }); + + it('no remainder', () => { + const d = new TimeDuration(1_234_000_000n); + equal(d.fdiv(1e6), 1234); + }); + + it('divide by -1', () => { + const d = new TimeDuration(1_234_567_890_987n); + equal(d.fdiv(-1), -1_234_567_890_987); + }); + + it('opposite sign', () => { + const d1 = new TimeDuration(1_234_567_890n); + checkFloat(d1.fdiv(-1e6), -1234.56789); + const d2 = new TimeDuration(-1_234_567_890n); + checkFloat(d2.fdiv(1e6), -1234.56789); + }); + + it('negative', () => { + const d = new TimeDuration(-1_234_567_890n); + checkFloat(d.fdiv(-1e6), 1234.56789); + }); + + it('quotient larger than seconds', () => { + const d = TimeDuration.normalize(25 + 5 * 24, 0, 86401, 333, 666, 999); + checkFloat(d.fdiv(86400e9), 7.041682102627303); + }); + + it('quotient smaller than seconds', () => { + const d = new TimeDuration(90061_333666999n); + checkFloat(d.fdiv(1000), 90061333666.999); + checkFloat(d.fdiv(10), 9006133366699.9); + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + checkFloat(d.fdiv(3), 30020444555666.333); + }); + + it('divide by 0', () => { + const d = new TimeDuration(90061_333666999n); + throws(() => d.fdiv(0), Error); + }); + }); + + it('isZero()', () => { + assert(new TimeDuration(0n).isZero()); + assert(!new TimeDuration(1_000_000_000n).isZero()); + assert(!new TimeDuration(-1n).isZero()); + assert(!new TimeDuration(1_000_000_001n).isZero()); + }); + + describe('round()', () => { + it('basic', () => { + const d = new TimeDuration(1_234_567_890n); + check(d.round(1000, 'halfExpand'), 1, 234568000); + }); + + it('increment 1', () => { + const d = new TimeDuration(1_234_567_890n); + check(d.round(1, 'ceil'), 1, 234567890); + }); + + it('rounds up from subseconds to seconds', () => { + const d = new TimeDuration(1_999_999_999n); + check(d.round(1e9, 'halfExpand'), 2, 0); + }); + + describe('Rounding modes', () => { + const increment = 100; + const testValues = [-150, -100, -80, -50, -30, 0, 30, 50, 80, 100, 150]; + const expectations = { + ceil: [-100, -100, 0, 0, 0, 0, 100, 100, 100, 100, 200], + floor: [-200, -100, -100, -100, -100, 0, 0, 0, 0, 100, 100], + trunc: [-100, -100, 0, 0, 0, 0, 0, 0, 0, 100, 100], + expand: [-200, -100, -100, -100, -100, 0, 100, 100, 100, 100, 200], + halfCeil: [-100, -100, -100, 0, 0, 0, 0, 100, 100, 100, 200], + halfFloor: [-200, -100, -100, -100, 0, 0, 0, 0, 100, 100, 100], + halfTrunc: [-100, -100, -100, 0, 0, 0, 0, 0, 100, 100, 100], + halfExpand: [-200, -100, -100, -100, 0, 0, 0, 100, 100, 100, 200], + halfEven: [-200, -100, -100, 0, 0, 0, 0, 0, 100, 100, 200] + }; + for (const roundingMode of Object.keys(expectations)) { + describe(roundingMode, () => { + testValues.forEach((value, ix) => { + const expected = expectations[roundingMode][ix]; + + it(`rounds ${value} ns to ${expected} ns`, () => { + const d = new TimeDuration(BigInt(value)); + const result = d.round(increment, roundingMode); + check(result, 0, expected); + }); + + it(`rounds ${value} s to ${expected} s`, () => { + const d = new TimeDuration(BigInt(value * 1e9)); + const result = d.round(increment * 1e9, roundingMode); + check(result, expected, 0); + }); + }); + }); + } + }); + }); + + it('sign()', () => { + equal(new TimeDuration(0n).sign(), 0); + equal(new TimeDuration(-1n).sign(), -1); + equal(new TimeDuration(-1_000_000_000n).sign(), -1); + equal(new TimeDuration(1n).sign(), 1); + equal(new TimeDuration(1_000_000_000n).sign(), 1); + }); + + describe('subtract', () => { + it('basic', () => { + const d1 = new TimeDuration(321_987654321n); + const d2 = new TimeDuration(123_123456789n); + check(d1.subtract(d2), 198, 864197532); + check(d2.subtract(d1), -198, -864197532); + }); + + it('signs differ in result', () => { + const d1 = new TimeDuration(3661_001001001n); + const d2 = new TimeDuration(86400_000_000_000n); + check(d1.subtract(d2), -82738, -998998999); + check(d2.subtract(d1), 82738, 998998999); + }); + }); +}); + +import { normalize } from 'path'; +if (normalize(import.meta.url.slice(8)) === normalize(process.argv[1])) { + report(reporter).then((failed) => process.exit(failed ? 1 : 0)); +} diff --git a/spec/abstractops.html b/spec/abstractops.html index 78ea5d61a7..a6b2d01a00 100644 --- a/spec/abstractops.html +++ b/spec/abstractops.html @@ -1618,7 +1618,18 @@

1. Let _factor_ be -1. 1. Else, 1. Let _factor_ be 1. - 1. Return ! CreateDurationRecord(_yearsMV_ × _factor_, _monthsMV_ × _factor_, _weeksMV_ × _factor_, _daysMV_ × _factor_, _hoursMV_ × _factor_, floor(_minutesMV_) × _factor_, floor(_secondsMV_) × _factor_, floor(_millisecondsMV_) × _factor_, floor(_microsecondsMV_) × _factor_, floor(_nanosecondsMV_) × _factor_). + 1. Set _yearsMV_ to _yearsMV_ × _factor_. + 1. Set _monthsMV_ to _monthsMV_ × _factor_. + 1. Set _weeksMV_ to _weeksMV_ × _factor_. + 1. Set _daysMV_ to _daysMV_ × _factor_. + 1. Set _hoursMV_ to _hoursMV_ × _factor_. + 1. Set _minutesMV_ to floor(_minutesMV_) × _factor_. + 1. Set _secondsMV_ to floor(_secondsMV_) × _factor_. + 1. Set _millisecondsMV_ to floor(_millisecondsMV_) × _factor_. + 1. Set _microsecondsMV_ to floor(_microsecondsMV_) × _factor_. + 1. Set _nanosecondsMV_ to floor(_nanosecondsMV_) × _factor_. + 1. If IsValidDuration(_yearsMV_, _monthsMV_, _weeksMV_, _daysMV_, _hoursMV_, _minutesMV_, _secondsMV_, _millisecondsMV_, _microsecondsMV_, _nanosecondsMV_) is *false*, throw a *RangeError* exception. + 1. Return CreateDurationRecord(_yearsMV_, _monthsMV_, _weeksMV_, _daysMV_, _hoursMV_, _minutesMV_, _secondsMV_, _millisecondsMV_, _microsecondsMV_, _nanosecondsMV_). diff --git a/spec/calendar.html b/spec/calendar.html index 7cc92d5e48..b2f02ecf85 100644 --- a/spec/calendar.html +++ b/spec/calendar.html @@ -1434,8 +1434,9 @@

Temporal.Calendar.prototype.dateAdd ( _date_, _duration_ [ , _options_ ] ) diff --git a/spec/duration.html b/spec/duration.html index 04004a0abc..11ad3f6c68 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -111,8 +111,10 @@

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

1. If _zonedRelativeTo_ is not *undefined*, and either _calendarUnitsPresent_ is *true*, or _one_.[[Days]] ≠ 0, or _two_.[[Days]] ≠ 0, then 1. Let _instant_ be ! CreateTemporalInstant(_zonedRelativeTo_.[[Nanoseconds]]). 1. Let _precalculatedPlainDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _instant_, _calendarRec_.[[Receiver]]). - 1. Let _after1_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _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]], _timeZoneRec_, _calendarRec_, _two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], _two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]], _precalculatedPlainDateTime_). + 1. Let _norm1_ be NormalizeTimeDuration(_one_.[[Hours]], _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]]). + 1. Let _norm2_ be NormalizeTimeDuration(_two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]]). + 1. Let _after1_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _one_.[[Years]], _one_.[[Months]], _one_.[[Weeks]], _one_.[[Days]], _norm1_, _precalculatedPlainDateTime_). + 1. Let _after2_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _two_.[[Years]], _two_.[[Months]], _two_.[[Weeks]], _two_.[[Days]], _norm2_, _precalculatedPlainDateTime_). 1. If _after1_ > _after2_, return *1*𝔽. 1. If _after1_ < _after2_, return *-1*𝔽. 1. Return *+0*𝔽. @@ -124,13 +126,11 @@

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

1. Else, 1. Let _days1_ be _one_.[[Days]]. 1. Let _days2_ be _two_.[[Days]]. - 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*𝔽. + 1. Let _norm1_ be NormalizeTimeDuration(_one_.[[Hours]], _one_.[[Minutes]], _one_.[[Seconds]], _one_.[[Milliseconds]], _one_.[[Microseconds]], _one_.[[Nanoseconds]]). + 1. Set _norm1_ to ! Add24HourDaysToNormalizedTimeDuration(_norm1_, _days1_). + 1. Let _norm2_ be NormalizeTimeDuration(_two_.[[Hours]], _two_.[[Minutes]], _two_.[[Seconds]], _two_.[[Milliseconds]], _two_.[[Microseconds]], _two_.[[Nanoseconds]]). + 1. Set _norm2_ to ! Add24HourDaysToNormalizedTimeDuration(_norm2_, _days2_). + 1. Return 𝔽(CompareNormalizedTimeDuration(_norm1_, _norm2_)). @@ -483,13 +483,15 @@

Temporal.Duration.prototype.round ( _roundTo_ )

1. If _largestUnitIsCalendarUnit_ is *true*, or _smallestUnitIsCalendarUnit_ is *true*, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateUntil~). 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _plainRelativeTo_, _calendarRec_). - 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_, _calendarRec_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _norm_, _roundingIncrement_, _smallestUnit_, _roundingMode_, _plainRelativeTo_, _calendarRec_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _roundResult_ be _roundRecord_.[[NormalizedDuration]]. 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_, _calendarRec_, _timeZoneRec_, _precalculatedPlainDateTime_). - 1. Let _balanceResult_ be ? BalanceTimeDurationRelative(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Set _roundResult_ to ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[NormalizedTime]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedRelativeTo_, _calendarRec_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _balanceResult_ be ? BalanceTimeDurationRelative(_roundResult_.[[Days]], _roundResult_.[[NormalizedTime]], _largestUnit_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). 1. Else, - 1. Let _balanceResult_ be ? BalanceTimeDuration(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_). + 1. Let _normWithDays_ be ? Add24HourDaysToNormalizedTimeDuration(_roundResult_.[[NormalizedTime]], _roundResult_.[[Days]]). + 1. Let _balanceResult_ be BalanceTimeDuration(_normWithDays_, _largestUnit_). 1. Let _result_ be ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _balanceResult_.[[Days]], _largestUnit_, _plainRelativeTo_, _calendarRec_). 1. Return ! CreateTemporalDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]]). @@ -533,12 +535,32 @@

Temporal.Duration.prototype.total ( _totalOf_ )

1. If _unitIsCalendarUnit_ is *true*, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateUntil~). 1. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _plainRelativeTo_, _calendarRec_). + 1. Let _days_ be _unbalanceResult_.[[Days]]. 1. If _zonedRelativeTo_ is not *undefined*, then 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _calendarRec_, _timeZoneRec_, _unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], 0, _precalculatedPlainDateTime_). - 1. Let _balanceResult_ be ? BalanceTimeDurationRelative(_unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _unit_, _intermediate_, _timeZoneRec_). + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Let _startInstant_ be ! CreateTemporalInstant(_intermediate_.[[Nanoseconds]]). + 1. Let _startDateTime_ be *undefined*. + 1. If _days_ ≠ 0, then + 1. Set _startDateTime_ to ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, *"iso8601"*). + 1. Let _addResult_ be ? AddDaysToZonedDateTime(_startInstant_, _startDateTime_, _timeZoneRec_, *"iso8601"*, _days_). + 1. Let _intermediateNs_ be _addResult_.[[EpochNanoseconds]]. + 1. Else, + 1. Let _intermediateNs_ be _intermediate_.[[Nanoseconds]]. + 1. Let _endNs_ be AddInstant(_intermediateNs_, _norm_). + 1. Set _norm_ to NormalizedTimeDurationFromEpochNanosecondsDifference(_endNs_, _intermediateNs_). + 1. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + 1. If NormalizedTimeDurationIsZero(_norm_) is *false* and _startDateTime_ is *undefined*, set _startDateTime_ to ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, *"iso8601"*). + 1. Let _result_ be ? NormalizedTimeDurationToDays(_norm_, _intermediate_, _timeZoneRec_, _startDateTime_). + 1. Set _norm_ to _result_.[[Remainder]]. + 1. Set _days_ to _result_.[[Days]]. + 1. Else, + 1. Set _days_ to 0. 1. Else, - 1. Let _balanceResult_ be ! BalanceTimeDuration(_unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _unit_). - 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_, _calendarRec_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Set _norm_ to ! Add24HourDaysToNormalizedTimeDuration(_norm_, _days_). + 1. Set _days_ to 0. + 1. Let _roundRecord_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _days_, _norm_, 1, _unit_, *"trunc"*, _plainRelativeTo_, _calendarRec_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). 1. Return 𝔽(_roundRecord_.[[Total]]). @@ -559,14 +581,16 @@

Temporal.Duration.prototype.toString ( [ _options_ ] )

1. If _smallestUnit_ is *"hour"* or *"minute"*, throw a *RangeError* exception. 1. Let _precision_ be ToSecondsStringPrecisionRecord(_smallestUnit_, _digits_). 1. If _precision_.[[Unit]] is not *"nanosecond"* or _precision_.[[Increment]] ≠ 1, then + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). 1. Let _largestUnit_ be DefaultTemporalLargestUnit(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]]). - 1. Let _roundRecord_ be ? RoundDuration(0, 0, 0, 0, 0, 0, _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. - 1. Let _balanceResult_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _largestUnit_). - 1. Let _result_ be ! CreateDurationRecord(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]]). + 1. Let _roundRecord_ be ? RoundDuration(0, 0, 0, 0, _norm_, _precision_.[[Increment]], _precision_.[[Unit]], _roundingMode_). + 1. Set _norm_ to _roundRecord_.[[NormalizedDuration]].[[NormalizedTime]]. + 1. Let _result_ be BalanceTimeDuration(_norm_, _largestUnit_). + 1. Set _result_ to CreateDurationRecord(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]] + _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). 1. Else, 1. Let _result_ be _duration_. - 1. Return ! TemporalDurationToString(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]], _precision_.[[Precision]]). + 1. Let _normSeconds_ be NormalizeTimeDuration(0, 0, _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). + 1. Return ! TemporalDurationToString(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _normSeconds_, _precision_.[[Precision]]). @@ -578,7 +602,8 @@

Temporal.Duration.prototype.toJSON ( )

1. Let _duration_ be the *this* value. 1. Perform ? RequireInternalSlot(_duration_, [[InitializedTemporalDuration]]). - 1. Return ! TemporalDurationToString(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"auto"*). + 1. Let _normSeconds_ be NormalizeTimeDuration(0, 0, _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Return ! TemporalDurationToString(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _normSeconds_, *"auto"*). @@ -594,7 +619,8 @@

Temporal.Duration.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] ) 1. Let _duration_ be the *this* value. 1. Perform ? RequireInternalSlot(_duration_, [[InitializedTemporalDuration]]). - 1. Return ! TemporalDurationToString(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"auto"*). + 1. Let _normSeconds_ be NormalizeTimeDuration(0, 0, _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Return ! TemporalDurationToString(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _normSeconds_, *"auto"*). @@ -863,6 +889,93 @@

Partial Duration Records

+ +

Normalized Time Duration Records

+

+ A Normalized Time Duration Record is a Record value used to represent the portion of a Temporal.Duration object that deals with time units, but as a combined value. + Normalized Time Duration Records are produced by the abstract operation NormalizeTimeDuration, among others. +

+

+ Normalized Time Duration Records have the fields listed in . +

+ + + + + + + + + + + + +
Field NameValueMeaning
[[TotalNanoseconds]] + an integer in the inclusive interval from -maxTimeDuration to maxTimeDuration, where + + maxTimeDuration = 253 × 109 - 1 = 9,007,199,254,740,991,999,999,999 + + + The number of nanoseconds in the duration. +
+
+
+ + +

Normalized Duration Records

+

+ A Normalized Duration Record is a Record value used to represent the combination of a Date Duration Record with a Normalized Time Duration Record. + Such Records are used by operations that deal with both date and time portions of durations, such as RoundDuration. +

+

+ Normalized Duration Records have the fields listed in . +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameValueMeaning
[[Years]]a float64-representable integer + The number of years in the duration. +
[[Months]]a float64-representable integer + The number of months in the duration. +
[[Weeks]]a float64-representable integer + The number of weeks in the duration. +
[[Days]]a float64-representable integer + The number of days in the duration. +
[[NormalizedTime]]a Normalized Time Duration Record + The time portion of the duration. +
+
+
+

CreateDurationRecord ( @@ -876,14 +989,14 @@

_milliseconds_: an integer, _microseconds_: an integer, _nanoseconds_: an integer, - ): either a normal completion containing a Duration Record, or a throw completion + ): a Duration Record

description
- 1. If ! IsValidDuration(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_) is *false*, throw a *RangeError* exception. + 1. Assert: ! IsValidDuration(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_) is *true*. 1. Return the Record { [[Years]]: ℝ(𝔽(_years_)), [[Months]]: ℝ(𝔽(_months_)), @@ -953,6 +1066,54 @@

+ +

+ CreateNormalizedDurationRecord ( + _years_: an integer, + _months_: an integer, + _weeks_: an integer, + _days_: an integer, + _norm_: a Normalized Time Duration Record, + ): either a normal completion containing a Normalized Duration Record, or a throw completion +

+
+
description
+
+
+ + 1. Let _dateDurationRecord_ be ? CreateDateDurationRecord(_years_, _months_, _weeks_, _days_). + 1. Return the Record { + [[Years]]: _dateDurationRecord_.[[Years]], + [[Months]]: _dateDurationRecord_.[[Months]], + [[Weeks]]: _dateDurationRecord_.[[Weeks]], + [[Days]]: _dateDurationRecord_.[[Days]], + [[NormalizedTime]]: _norm_ + }. + +
+ + +

+ CombineDateAndNormalizedTimeDuration ( + _dateDurationRecord_: a Date Duration Record, + _norm_: a Normalized Time Duration Record, + ): a Normalized Duration Record +

+
+
description
+
+
+ + 1. Return the Record { + [[Years]]: _dateDurationRecord_.[[Years]], + [[Months]]: _dateDurationRecord_.[[Months]], + [[Weeks]]: _dateDurationRecord_.[[Weeks]], + [[Days]]: _dateDurationRecord_.[[Days]], + [[NormalizedTime]]: _norm_ + }. + +
+

ToTemporalDuration ( @@ -986,7 +1147,7 @@

1. If _temporalDurationLike_ is not a String, throw a *TypeError* exception. 1. Return ? ParseTemporalDurationString(_temporalDurationLike_). 1. If _temporalDurationLike_ has an [[InitializedTemporalDuration]] internal slot, then - 1. Return ! CreateDurationRecord(_temporalDurationLike_.[[Years]], _temporalDurationLike_.[[Months]], _temporalDurationLike_.[[Weeks]], _temporalDurationLike_.[[Days]], _temporalDurationLike_.[[Hours]], _temporalDurationLike_.[[Minutes]], _temporalDurationLike_.[[Seconds]], _temporalDurationLike_.[[Milliseconds]], _temporalDurationLike_.[[Microseconds]], _temporalDurationLike_.[[Nanoseconds]]). + 1. Return CreateDurationRecord(_temporalDurationLike_.[[Years]], _temporalDurationLike_.[[Months]], _temporalDurationLike_.[[Weeks]], _temporalDurationLike_.[[Days]], _temporalDurationLike_.[[Hours]], _temporalDurationLike_.[[Minutes]], _temporalDurationLike_.[[Seconds]], _temporalDurationLike_.[[Milliseconds]], _temporalDurationLike_.[[Microseconds]], _temporalDurationLike_.[[Nanoseconds]]). 1. Let _result_ be a new Duration Record with each field set to 0. 1. Let _partial_ be ? ToTemporalPartialDurationRecord(_temporalDurationLike_). 1. If _partial_.[[Years]] is not *undefined*, set _result_.[[Years]] to _partial_.[[Years]]. @@ -1188,21 +1349,23 @@

- +

- TotalDurationNanoseconds ( + NormalizeTimeDuration ( _hours_: an integer, _minutes_: an integer, _seconds_: an integer, _milliseconds_: an integer, _microseconds_: an integer, _nanoseconds_: an integer, - ): an integer + ): a Normalized Time Duration Record

description
- It computes an integer number of nanoseconds from the given units. + From the given units, it computes a normalized time duration consisting of whole seconds, and subseconds expressed in nanoseconds. + The normalized time duration can be stored losslessly in two 64-bit floating point numbers. + Alternatively, _normalizedSeconds_ × 109 + _subseconds_ can be stored as a 96-bit integer.
@@ -1210,33 +1373,268 @@

1. Set _seconds_ to _seconds_ + _minutes_ × 60. 1. Set _milliseconds_ to _milliseconds_ + _seconds_ × 1000. 1. Set _microseconds_ to _microseconds_ + _milliseconds_ × 1000. - 1. Return _nanoseconds_ + _microseconds_ × 1000. + 1. Set _nanoseconds_ to _nanoseconds_ + _microseconds_ × 1000. + 1. Assert: abs(_nanoseconds_) ≤ maxTimeDuration. + 1. Return the Record { [[TotalNanoseconds]]: _nanoseconds_ }. + + + + +

+ NormalizedTimeDurationAbs ( + _d_: a Normalized Time Duration Record, + ): a Normalized Time Duration Record +

+
+
description
+
It returns a new normalized time duration that is the absolute value of _d_.
+
+ + 1. Return the Record { [[TotalNanoseconds]]: abs(_d_.[[TotalNanoseconds]]) }. + +
+ + +

+ AddNormalizedTimeDuration ( + _one_: a Normalized Time Duration Record, + _two_: a Normalized Time Duration Record, + ): either a normal completion containing a Normalized Time Duration Record, or a throw completion +

+
+
description
+
It returns a normalized time duration that is the sum of _one_ and _two_, throwing an exception if the result is greater than the maximum normalized time duration.
+
+ + 1. Let _result_ be _one_.[[TotalNanoseconds]] + _two_.[[TotalNanoseconds]]. + 1. If abs(_result_) > maxTimeDuration, throw a *RangeError* exception. + 1. Return the Record { [[TotalNanoseconds]]: _result_ }. + +
+ + +

+ Add24HourDaysToNormalizedTimeDuration ( + _d_: a Normalized Time Duration Record, + _days_: an integer, + ): either a normal completion containing a Normalized Time Duration Record, or a throw completion +

+
+
description
+
+ It returns a normalized time duration that is the sum of _d_ and the number of 24-hour days indicated by _days_, throwing an exception if the result is greater than the maximum normalized time duration. + This operation should not be used when adding days relative to a Temporal.ZonedDateTime, since the days may not be 24 hours long. +
+
+ + 1. Let _result_ be _d_.[[TotalNanoseconds]] + _days_ × nsPerDay. + 1. If abs(_result_) > maxTimeDuration, throw a *RangeError* exception. + 1. Return the Record { [[TotalNanoseconds]]: _result_ }. + +
+ + +

+ AddNormalizedTimeDurationToEpochNanoseconds ( + _d_: a Normalized Time Duration Record, + _epochNs_: a BigInt, + ): a BigInt +

+
+
description
+
+ It adds a normalized time duration _d_ to an exact time in nanoseconds since the epoch, _epochNs_, and returns a new exact time. + The returned exact time is not required to be valid according to IsValidEpochNanoseconds. +
+
+ + 1. Return _epochNs_ + ℤ(_d_.[[TotalNanoseconds]]). + +
+ + +

+ CompareNormalizedTimeDuration ( + _one_: a Normalized Time Duration Record, + _two_: a Normalized Time Duration Record, + ): -1, 0, or 1 +

+
+
description
+
It performs a comparison of two Normalized Time Duration Records.
+
+ + 1. If _one_.[[TotalNanoseconds]] > _two_.[[TotalNanoseconds]], return 1. + 1. If _one_.[[TotalNanoseconds]] < _two_.[[TotalNanoseconds]], return -1. + 1. Return 0. + +
+ + +

+ DivideNormalizedTimeDuration ( + _d_: a Normalized Time Duration Record, + _divisor_: an integer, + ): a mathematical value +

+
+
description
+
It divides the total number of nanoseconds in the normalized time duration _d_ by _divisor_.
+
+ + 1. Assert: _divisor_ ≠ 0. + 1. Return _d_.[[TotalNanoseconds]] / _divisor_. + +
+ + +

+ NormalizedTimeDurationFromEpochNanosecondsDifference ( + _one_: a BigInt, + _two_: a BigInt, + ): a Normalized Time Duration Record +

+
+
description
+
It creates a Normalized Time Duration Record with the difference between two exact times in nanoseconds since the epoch, which must not be greater than the maximum normalized time duration.
+
+ + 1. Let _result_ be ℝ(_one_) - ℝ(_two_). + 1. Assert: abs(_result_) ≤ maxTimeDuration. + 1. Return the Record { [[TotalNanoseconds]]: _result_ }. + +
+ + +

+ NormalizedTimeDurationIsZero ( + _d_: a Normalized Time Duration Record, + ): a Boolean +

+
+
description
+
It returns *true* if _d_ is a zero duration, *false* otherwise.
+
+ + 1. If _d_.[[TotalNanoseconds]] = 0, return *true*. + 1. Return *false*. + +
+ + +

+ RoundNormalizedTimeDurationToIncrement ( + _d_: a Normalized Time Duration Record, + _increment_: an integer, + _roundingMode_: *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, or *"halfEven"*, + ): a Normalized Time Duration Record +

+
+
description
+
It rounds the total number of nanoseconds in the normalized time duration _d_ to the nearest multiple of _increment_, up or down according to _roundingMode_.
+
+ + 1. Let _rounded_ be RoundNumberToIncrement(_d_.[[TotalNanoseconds]], _increment_, _roundingMode_). + 1. Return the Record { [[TotalNanoseconds]]: _rounded_ }. + +
+ + +

+ NormalizedTimeDurationSeconds ( + _d_: a Normalized Time Duration Record, + ): an integral Number +

+
+
description
+
It returns the integer number of seconds in _d_, as a Number.
+
+ + 1. Let _seconds_ be truncate(_d_.[[TotalNanoseconds]] / 109). + 1. Assert: _seconds_ < 253. + 1. Return 𝔽(_seconds_). + +
+ + +

+ NormalizedTimeDurationSign ( + _d_: a Normalized Time Duration Record, + ): -1, 0, or 1 +

+
+
description
+
It returns 0 if the duration is zero, or ±1 depending on the sign of the duration.
+
+ + 1. If _d_.[[TotalNanoseconds]] < 0, return -1. + 1. If _d_.[[TotalNanoseconds]] > 0, return 1. + 1. Return 0. + +
+ + +

+ NormalizedTimeDurationSubseconds ( + _d_: a Normalized Time Duration Record, + ): an integral Number in the inclusive range *-999999999*𝔽 to *999999999*𝔽 +

+
+
description
+
It returns the integer number of nanoseconds in the subsecond part of _d_, as a Number.
+
+ + 1. Return 𝔽(remainder(_d_.[[TotalNanoseconds]], 109)). + +
+ + +

+ SubtractNormalizedTimeDuration ( + _one_: a Normalized Time Duration Record, + _two_: a Normalized Time Duration Record, + ): either a normal completion containing a Normalized Time Duration Record, or a throw completion +

+
+
description
+
It returns a normalized time duration that is the difference between _one_ and _two_, throwing an exception if the result is greater than the maximum normalized time duration.
+
+ + 1. Let _result_ be _one_.[[TotalNanoseconds]] - _two_.[[TotalNanoseconds]]. + 1. If abs(_result_) > maxTimeDuration, throw a *RangeError* exception. + 1. Return the Record { [[TotalNanoseconds]]: _result_ }. + +
+ + +

+ ZeroTimeDuration ( + ): a Normalized Time Duration Record +

+
+
description
+
It returns a normalized time duration of zero length.
+
+ + 1. Return the Record { [[TotalNanoseconds]]: 0 }.

BalanceTimeDuration ( - _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, _largestUnit_: a String, - ): either a normal completion containing a Time Duration Record, or an abrupt completion + ): a Time Duration Record

description
-
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 abruptly with a *RangeError*.
+
It converts a normalized time duration into a time duration with separated units, up to _largestUnit_.
- 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_). + 1. Let _days_, _hours_, _minutes_, _seconds_, _milliseconds_, and _microseconds_ be 0. + 1. Let _sign_ be NormalizedTimeDurationSign(_norm_). + 1. Let _nanoseconds_ be NormalizedTimeDurationAbs(_norm_).[[TotalNanoseconds]]. 1. If _largestUnit_ is *"year"*, *"month"*, *"week"*, or *"day"*, then 1. Set _microseconds_ to floor(_nanoseconds_ / 1000). 1. Set _nanoseconds_ to _nanoseconds_ modulo 1000. @@ -1287,7 +1685,7 @@

1. Set _nanoseconds_ to _nanoseconds_ modulo 1000. 1. Else, 1. Assert: _largestUnit_ is *"nanosecond"*. - 1. Return ? CreateTimeDurationRecord(_days_ × _sign_, _hours_ × _sign_, _minutes_ × _sign_, _seconds_ × _sign_, _milliseconds_ × _sign_, _microseconds_ × _sign_, _nanoseconds_ × _sign_). + 1. Return ! CreateTimeDurationRecord(_days_ × _sign_, _hours_ × _sign_, _minutes_ × _sign_, _seconds_ × _sign_, _milliseconds_ × _sign_, _microseconds_ × _sign_, _nanoseconds_ × _sign_). @@ -1295,16 +1693,11 @@

BalanceTimeDurationRelative ( _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, _largestUnit_: a String, _zonedRelativeTo_: a Temporal.ZonedDateTime, _timeZoneRec_: a Time Zone Methods Record, - optional _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, + _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, ): either a normal completion containing either Time Duration Record if there are no infinite values, ~positive overflow~, or ~negative overflow~ in case of infinite values, or an abrupt completion

@@ -1315,24 +1708,25 @@

This operation observably calls time zone and calendar methods.

- 1. If _precalculatedPlainDateTime_ is not present, let _precalculatedPlainDateTime_ be *undefined*. - 1. Let _intermediateNs_ be _zonedRelativeTo_.[[Nanoseconds]]. - 1. Let _startInstant_ be ! CreateTemporalInstant(_zonedRelativeTo_.[[Nanoseconds]]). + 1. Let _startNs_ be _zonedRelativeTo_.[[Nanoseconds]]. + 1. Let _startInstant_ be ! CreateTemporalInstant(_startNs_). + 1. Let _intermediateNs_ be _startNs_. 1. If _days_ ≠ 0, then 1. If _precalculatedPlainDateTime_ is *undefined*, set _precalculatedPlainDateTime_ to ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, *"iso8601"*). 1. Let _intermediateResult_ be ? AddDaysToZonedDateTime(_startInstant_, _precalculatedPlainDateTime_, _timeZoneRec_, *"iso8601"*, _days_). 1. Set _intermediateNs_ to _intermediateResult_.[[EpochNanoseconds]]. - 1. Let _endNs_ be AddInstant(_intermediateNs_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Set _nanoseconds_ to ℝ(_endNs_ - _zonedRelativeTo_.[[Nanoseconds]]). - 1. If _nanoseconds_ = 0, return ! CreateTimeDurationRecord(0, 0, 0, 0, 0, 0, 0). + 1. Let _endNs_ be AddInstant(_intermediateNs_, _norm_). + 1. Set _norm_ to NormalizedTimeDurationFromEpochNanosecondsDifference(_endNs_, _startNs_). + 1. If NormalizedTimeDurationIsZero(_norm_) is *true*, return ! CreateTimeDurationRecord(0, 0, 0, 0, 0, 0, 0). 1. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then 1. If _precalculatedPlainDateTime_ is *undefined*, set _precalculatedPlainDateTime_ to ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, *"iso8601"*). - 1. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _result_ be ? NormalizedTimeDurationToDays(_norm_, _zonedRelativeTo_, _timeZoneRec_, _precalculatedPlainDateTime_). 1. Set _days_ to _result_.[[Days]]. + 1. Set _norm_ to _result_.[[NormalizedTime]]. 1. Set _largestUnit_ to *"hour"*. 1. Else, 1. Set _days_ to 0. - 1. Let _balanceResult_ be ? BalanceTimeDuration(0, 0, 0, 0, 0, 0, _result_.[[Nanoseconds]], _largestUnit_). + 1. Let _balanceResult_ be BalanceTimeDuration(_norm_, _largestUnit_). 1. Return ! CreateTimeDurationRecord(_days_, _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]]). @@ -1485,8 +1879,12 @@

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. Let _norm1_ be NormalizeTimeDuration(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_). + 1. Let _norm2_ be NormalizeTimeDuration(_h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + 1. Let _normResult_ be ? AddNormalizedTimeDuration(_norm1_, _norm2_). + 1. Set _normResult_ to ? Add24HourDaysToNormalizedTimeDuration(_normResult_, _d1_ + _d2_). + 1. Let _result_ be BalanceTimeDuration(_normResult_, _largestUnit_). + 1. Return CreateDurationRecord(0, 0, 0, _result_.[[Days]], _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). 1. If _plainRelativeTo_ is not *undefined*, then 1. Assert: _zonedRelativeTo_ is *undefined*. 1. Let _dateDuration1_ be ! CreateTemporalDuration(_y1_, _mon1_, _w1_, _d1_, 0, 0, 0, 0, 0, 0). @@ -1499,8 +1897,12 @@

1. Let _differenceOptions_ be OrdinaryObjectCreate(*null*). 1. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). 1. Let _dateDifference_ be ? DifferenceDate(_calendarRec_, _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. Let _norm1_ be NormalizeTimeDuration(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_). + 1. Let _norm2_ be NormalizeTimeDuration(_h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + 1. Let _norm1WithDays_ be ? Add24HourDaysToNormalizedTimeDuration(_norm1_, _dateDifference_.[[Days]]). + 1. Let _normResult_ be ? AddNormalizedTimeDuration(_norm1WithDays_, _norm2_). + 1. Let _result_ be BalanceTimeDuration(_normResult_, _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: _zonedRelativeTo_ is not *undefined*. 1. If _precalculatedPlainDateTime_ is not present, let _precalculatedPlainDateTime_ be *undefined*. 1. If _largestUnit_ is *"year"*, or _largestUnit_ is *"month"*, or _largestUnit_ is *"week"*, or _largestUnit_ is *"day"*, let _startDateTimeNeeded_ be *true*; else let _startDateTimeNeeded_ be *false*. @@ -1508,12 +1910,17 @@

1. Let _startDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _zonedRelativeTo_.[[Nanoseconds]], _calendarRec_.[[Receiver]]). 1. Else, 1. Let _startDateTime_ be _precalculatedPlainDateTime_. - 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _y1_, _mon1_, _w1_, _d1_, _h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _startDateTime_). - 1. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZoneRec_, _calendarRec_, _y2_, _mon2_, _w2_, _d2_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + 1. Let _norm1_ be NormalizeTimeDuration(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_). + 1. Let _norm2_ be NormalizeTimeDuration(_h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _y1_, _mon1_, _w1_, _d1_, _norm1_, _startDateTime_). + 1. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZoneRec_, _calendarRec_, _y2_, _mon2_, _w2_, _d2_, _norm2_). 1. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then - 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(_zonedRelativeTo_.[[Nanoseconds]], _endNs_, _timeZoneRec_, _calendarRec_, _largestUnit_, OrdinaryObjectCreate(*null*), _startDateTime_). + 1. Let _norm_ be NormalizedTimeDurationFromEpochNanosecondsDifference(_endNs_, _zonedRelativeTo_.[[Nanoseconds]]). + 1. Let _result_ be BalanceTimeDuration(_norm_, _largestUnit_). + 1. Return CreateDurationRecord(0, 0, 0, 0, _result_.[[Hours]], _result_.[[Minutes]], _result_.[[Seconds]], _result_.[[Milliseconds]], _result_.[[Microseconds]], _result_.[[Nanoseconds]]). + 1. Let _diffResult_ be ? DifferenceZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _endNs_, _timeZoneRec_, _calendarRec_, _largestUnit_, OrdinaryObjectCreate(*null*), _startDateTime_). + 1. Let _timeResult_ be BalanceTimeDuration(_diffResult_.[[NormalizedTime]], *"hour"*). + 1. Return CreateDurationRecord(_diffResult_.[[Years]], _diffResult_.[[Months]], _diffResult_.[[Weeks]], _diffResult_.[[Days]], _timeResult_.[[Hours]], _timeResult_.[[Minutes]], _timeResult_.[[Seconds]], _timeResult_.[[Milliseconds]], _timeResult_.[[Microseconds]], _timeResult_.[[Nanoseconds]]). @@ -1581,7 +1988,7 @@

1. Assert: TimeZoneMethodsRecordHasLookedUp(_timeZoneRec_, ~getOffsetNanosecondsFor~) is *true*. 1. Assert: TimeZoneMethodsRecordHasLookedUp(_timeZoneRec_, ~getPossibleInstantsFor~) is *true*. - 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, _precalculatedPlainDateTime_). + 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _years_, _months_, _weeks_, _days_, ZeroTimeDuration(), _precalculatedPlainDateTime_). 1. Return ! CreateTemporalZonedDateTime(_intermediateNs_, _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]]). @@ -1593,12 +2000,7 @@

_months_: an integer, _weeks_: an integer, _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, _increment_: an integer, _unit_: a String, _roundingMode_: a String, @@ -1607,7 +2009,7 @@

optional _zonedRelativeTo_: *undefined* or a Temporal.ZonedDateTime, optional _timeZoneRec_: *undefined* or a Time Zone Methods Record, optional _precalculatedPlainDateTime_: *undefined* or a Temporal.PlainDateTime, - ): either a normal completion containing a Record with fields [[DurationRecord]] (a Duration Record) and [[Total]] (a mathematical value), or a throw completion + ): either a normal completion containing a Record with fields [[NormalizedDuration]] (a Normalized Duration Record) and [[Total]] (a mathematical value), or a throw completion

description
@@ -1626,17 +2028,14 @@

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(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). 1. If _zonedRelativeTo_ is not *undefined*, then 1. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _calendarRec_, _timeZoneRec_, _years_, _months_, _weeks_, _days_, _precalculatedPlainDateTime_). - 1. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_, _timeZoneRec_). - 1. Let _fractionalDays_ be _days_ + _result_.[[Days]] + _result_.[[Nanoseconds]] / _result_.[[DayLength]]. + 1. Let _result_ be ? NormalizedTimeDurationToDays(_norm_, _intermediate_, _timeZoneRec_). + 1. Let _fractionalDays_ be _days_ + _result_.[[Days]] + DivideNormalizedTimeDuration(_result_.[[Remainder]], _result_.[[DayLength]]). 1. Else, - 1. Let _fractionalDays_ be _days_ + _nanoseconds_ / nsPerDay. - 1. Set _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0. - 1. [declared="fractionalSeconds"] Assert: _fractionalSeconds_ is not used below. + 1. Let _fractionalDays_ be _days_ + DivideNormalizedTimeDuration(_norm_, nsPerDay). + 1. Set _days_ to 0. 1. Else, - 1. Let _fractionalSeconds_ be _nanoseconds_ × 10-9 + _microseconds_ × 10-6 + _milliseconds_ × 10-3 + _seconds_. 1. Assert: _fractionalDays_ is not used below. 1. Let _total_ be ~unset~. 1. If _unit_ is *"year"*, then @@ -1670,6 +2069,7 @@

1. Set _years_ to RoundNumberToIncrement(_fractionalYears_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalYears_. 1. Set _months_ and _weeks_ to 0. + 1. Set _norm_ to ZeroTimeDuration(). 1. Else if _unit_ is *"month"*, then 1. Assert: CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateAdd~) is *true*. 1. Assert: CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateUntil~) is *true*. @@ -1701,6 +2101,7 @@

1. Set _months_ to RoundNumberToIncrement(_fractionalMonths_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalMonths_. 1. Set _weeks_ to 0. + 1. Set _norm_ to ZeroTimeDuration(). 1. Else if _unit_ is *"week"*, then 1. Assert: CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateAdd~) is *true*. 1. Assert: CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateUntil~) is *true*. @@ -1724,40 +2125,37 @@

1. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_). 1. Set _weeks_ to RoundNumberToIncrement(_fractionalWeeks_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalWeeks_. + 1. Set _norm_ to ZeroTimeDuration(). 1. Else if _unit_ is *"day"*, then 1. Set _days_ to RoundNumberToIncrement(_fractionalDays_, _increment_, _roundingMode_). 1. Set _total_ to _fractionalDays_. + 1. Set _norm_ to ZeroTimeDuration(). 1. Else if _unit_ is *"hour"*, then - 1. Let _fractionalHours_ be (_fractionalSeconds_ / 60 + _minutes_) / 60 + _hours_. - 1. Set _hours_ to RoundNumberToIncrement(_fractionalHours_, _increment_, _roundingMode_). - 1. Set _total_ to _fractionalHours_. - 1. Set _minutes_, _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0. + 1. Let _divisor_ be 3.6 × 1012. + 1. Set _total_ to DivideNormalizedTimeDuration(_norm_, _divisor_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _divisor_ × _increment_, _roundingMode_). 1. Else if _unit_ is *"minute"*, then - 1. Let _fractionalMinutes_ be _fractionalSeconds_ / 60 + _minutes_. - 1. Set _minutes_ to RoundNumberToIncrement(_fractionalMinutes_, _increment_, _roundingMode_). - 1. Set _total_ to _fractionalMinutes_. - 1. Set _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ to 0. + 1. Let _divisor_ be 6 × 1010. + 1. Set _total_ to DivideNormalizedTimeDuration(_norm_, _divisor_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _divisor_ × _increment_, _roundingMode_). 1. Else if _unit_ is *"second"*, then - 1. Set _seconds_ to RoundNumberToIncrement(_fractionalSeconds_, _increment_, _roundingMode_). - 1. Set _total_ to _fractionalSeconds_. - 1. Set _milliseconds_, _microseconds_, and _nanoseconds_ to 0. + 1. Let _divisor_ be 109. + 1. Set _total_ to DivideNormalizedTimeDuration(_norm_, _divisor_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _divisor_ × _increment_, _roundingMode_). 1. Else if _unit_ is *"millisecond"*, then - 1. Let _fractionalMilliseconds_ be _nanoseconds_ × 10-6 + _microseconds_ × 10-3 + _milliseconds_. - 1. Set _milliseconds_ to RoundNumberToIncrement(_fractionalMilliseconds_, _increment_, _roundingMode_). - 1. Set _total_ to _fractionalMilliseconds_. - 1. Set _microseconds_ and _nanoseconds_ to 0. + 1. Let _divisor_ be 106. + 1. Set _total_ to DivideNormalizedTimeDuration(_norm_, _divisor_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _divisor_ × _increment_, _roundingMode_). 1. Else if _unit_ is *"microsecond"*, then - 1. Let _fractionalMicroseconds_ be _nanoseconds_ × 10-3 + _microseconds_. - 1. Set _microseconds_ to RoundNumberToIncrement(_fractionalMicroseconds_, _increment_, _roundingMode_). - 1. Set _total_ to _fractionalMicroseconds_. - 1. Set _nanoseconds_ to 0. + 1. Let _divisor_ be 103. + 1. Set _total_ to DivideNormalizedTimeDuration(_norm_, _divisor_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _divisor_ × _increment_, _roundingMode_). 1. Else, 1. Assert: _unit_ is *"nanosecond"*. - 1. Set _total_ to _nanoseconds_. - 1. Set _nanoseconds_ to RoundNumberToIncrement(_nanoseconds_, _increment_, _roundingMode_). - 1. Let _duration_ be ? CreateDurationRecord(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Set _total_ to NormalizedTimeDurationSeconds(_norm_) × 109 + NormalizedTimeDurationSubseconds(_norm_). + 1. Set _norm_ to RoundNormalizedTimeDurationToIncrement(_norm_, _increment_, _roundingMode_). 1. Return the Record { - [[DurationRecord]]: _duration_, + [[NormalizedDuration]]: ? CreateNormalizedDurationRecord(_years_, _months_, _weeks_, _days_, _norm_), [[Total]]: _total_ }. @@ -1770,12 +2168,7 @@

_months_: an integer, _weeks_: an integer, _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, _increment_: an integer, _unit_: a String, _roundingMode_: a String, @@ -1783,7 +2176,7 @@

_calendarRec_: a Calendar Methods Record, _timeZoneRec_: a Time Zone Methods Record, _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, - ): either a normal completion containing a Duration Record, or a throw completion + ): either a normal completion containing a Normalized Duration Record, or a throw completion

description
@@ -1795,24 +2188,20 @@

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. Return ! CreateNormalizedDurationRecord(_years_, _months_, _weeks_, _days_, _norm_). 1. Assert: _precalculatedPlainDateTime_ is not *undefined*. - 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. - 1. Let _dayStart_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, _precalculatedPlainDateTime_). + 1. Let _direction_ be NormalizedTimeDurationSign(_norm_). + 1. Let _dayStart_ be ? AddZonedDateTime(_zonedRelativeTo_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _years_, _months_, _weeks_, _days_, ZeroTimeDuration(), _precalculatedPlainDateTime_). 1. Let _dayStartInstant_ be ! CreateTemporalInstant(_dayStart_). 1. Let _dayStartDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _dayStartInstant_, _zonedRelativeTo_.[[Calendar]]). 1. Let _dayEnd_ be ? AddDaysToZonedDateTime(_dayStartInstant_, _dayStartDateTime_, _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _direction_).[[EpochNanoseconds]]. - 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 _dayLengthNs_ be NormalizedTimeDurationFromEpochNanosecondsDifference(_dayEnd_, _dayStart_). + 1. Let _oneDayLess_ be ? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_). + 1. If NormalizedTimeDurationSign(_oneDayLess_) × _direction_ < 0, then + 1. Return ! CreateNormalizedDurationRecord(_years_, _months_, _weeks_, _days_, _norm_). 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_, _calendarRec_, _timeZoneRec_, _precalculatedPlainDateTime_). - 1. Let _adjustedTimeDuration_ be ! RoundDuration(0, 0, 0, 0, 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]]). + 1. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _oneDayLess_, _increment_, _unit_, _roundingMode_). + 1. Return CombineDateAndNormalizedTimeDuration(_adjustedDateDuration_, _roundRecord_.[[NormalizedDuration]].[[NormalizedTime]]). @@ -1825,10 +2214,7 @@

_days_: an integer, _hours_: an integer, _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _normSeconds_: a Normalized Time Duration Record, _precision_: an integer between 0 and 9 inclusive, or *"auto"*, )

@@ -1837,13 +2223,7 @@

It returns a String which is the ISO 8601 representation of the duration denoted by _years_ through _nanoseconds_, with the number of decimal places in the seconds value controlled by _precision_.

- 1. Let _sign_ be ! DurationSign(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Set _microseconds_ to _microseconds_ + truncate(_nanoseconds_ / 1000). - 1. Set _nanoseconds_ to remainder(_nanoseconds_, 1000). - 1. Set _milliseconds_ to _milliseconds_ + truncate(_microseconds_ / 1000). - 1. Set _microseconds_ to remainder(_microseconds_, 1000). - 1. Set _seconds_ to _seconds_ + truncate(_milliseconds_ / 1000). - 1. Set _milliseconds_ to remainder(_milliseconds_, 1000). + 1. Let _sign_ be ! DurationSign(_years_, _months_, _weeks_, _days_, _hours_, _minutes_, NormalizedTimeDurationSeconds(_normSeconds_), 0, 0, NormalizedTimeDurationSubseconds(_normSeconds_)). 1. Let _datePart_ be *""*. 1. If _years_ is not 0, then 1. Set _datePart_ to the string concatenation of abs(_years_) formatted as a decimal number and the code unit 0x0059 (LATIN CAPITAL LETTER Y). @@ -1858,14 +2238,11 @@

1. Set _timePart_ to the string concatenation of abs(_hours_) formatted as a decimal number and the code unit 0x0048 (LATIN CAPITAL LETTER H). 1. If _minutes_ is not 0, then 1. Set _timePart_ to the string concatenation of _timePart_, abs(_minutes_) formatted as a decimal number, and the code unit 0x004D (LATIN CAPITAL LETTER M). - 1. Let _nonzeroSecondsAndLower_ be *false*. - 1. If _seconds_ ≠ 0, or _milliseconds_ ≠ 0, or _microseconds_ ≠ 0, or _nanoseconds_ ≠ 0, set _nonzeroSecondsAndLower_ to *true*. 1. Let _zeroMinutesAndHigher_ be *false*. 1. If _years_ = 0, and _months_ = 0, and _weeks_ = 0, and _days_ = 0, and _hours_ = 0, and _minutes_ = 0, set _zeroMinutesAndHigher_ to *true*. - 1. If _nonzeroSecondsAndLower_ is *true*, or _zeroMinutesAndHigher_ is *true*, or _precision_ is not *"auto"*, then - 1. Let _secondsPart_ be abs(_seconds_) formatted as a decimal number. - 1. Let _subSecondNanoseconds_ be abs(_milliseconds_) × 106 + abs(_microseconds_) × 103 + abs(_nanoseconds_). - 1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision_). + 1. If NormalizedTimeDurationIsZero(_normSeconds_) is *false*, or _zeroMinutesAndHigher_ is *true*, or _precision_ is not *"auto"*, then + 1. Let _secondsPart_ be abs(ℝ(NormalizedTimeDurationSeconds(_normSeconds_))) formatted as a decimal number. + 1. Let _subSecondsPart_ be FormatFractionalSeconds(abs(ℝ(NormalizedTimeDurationSubseconds(_normSeconds_))), _precision_). 1. Set _timePart_ to the string concatenation of _timePart_, _secondsPart_, _subSecondsPart_, and the code unit 0x0053 (LATIN CAPITAL LETTER S). 1. Let _signPart_ be the code unit 0x002D (HYPHEN-MINUS) if _sign_ < 0, and otherwise the empty String. 1. Let _result_ be the string concatenation of _signPart_, the code unit 0x0050 (LATIN CAPITAL LETTER P) and _datePart_. diff --git a/spec/instant.html b/spec/instant.html index 1fee02468e..ef15942942 100644 --- a/spec/instant.html +++ b/spec/instant.html @@ -551,12 +551,7 @@

AddInstant ( _epochNanoseconds_: a BigInt value, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, )

@@ -564,13 +559,8 @@

It adds a duration in various time units to a number of nanoseconds since the epoch, and returns the result as a BigInt value.

- 1. Let _result_ be _epochNanoseconds_ + ℤ(_nanoseconds_) + - ℤ(_microseconds_) × *1000* + - ℤ(_milliseconds_) × ℤ(106) + - ℤ(_seconds_) × ℤ(109) + - ℤ(_minutes_) × *60* × ℤ(109) + - ℤ(_hours_) × *3600* × ℤ(109). - 1. If IsValidEpochNanoseconds(_result_) is *false*, throw a *RangeError* exception. + 1. Let _result_ be AddNormalizedTimeDurationToEpochNanoseconds(_norm_, _epochNanoseconds_). + 1. If ! IsValidEpochNanoseconds(_result_) is *false*, throw a *RangeError* exception. 1. Return _result_. @@ -582,25 +572,19 @@

_ns2_: a BigInt, _roundingIncrement_: a positive integer, _smallestUnit_: a String, - _largestUnit_: a String, _roundingMode_: a String, - ): a Time Duration Record + ): a Normalized Time Duration Record

description
-
It computes the difference between two exact times _ns1_ and _ns2_ expressed in nanoseconds since the epoch, and rounds the result according to the parameters _roundingIncrement_, _smallestUnit_, _largestUnit_, and _roundingMode_.
+
It computes the difference between two exact times _ns1_ and _ns2_ expressed in nanoseconds since the epoch, and rounds the result according to the parameters _roundingIncrement_, _smallestUnit_, and _roundingMode_.
- 1. Let _difference_ be ℝ(_ns2_) - ℝ(_ns1_). - 1. Let _nanoseconds_ be remainder(_difference_, 1000). - 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. Let _difference_ be NormalizedTimeDurationFromEpochNanosecondsDifference(_ns2_, _ns1_). 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_). + 1. Return _difference_. + 1. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _difference_, _roundingIncrement_, _smallestUnit_, _roundingMode_). + 1. Return _roundRecord_.[[NormalizedDuration]].[[NormalizedTime]]. @@ -671,7 +655,8 @@

1. Set _other_ to ? ToTemporalInstant(_other_). 1. Let _resolvedOptions_ be ? SnapshotOwnProperties(? GetOptionsObject(_options_), *null*). 1. Let _settings_ be ? GetDifferenceSettings(_operation_, _resolvedOptions_, ~time~, « », *"nanosecond"*, *"second"*). - 1. Let _result_ be DifferenceInstant(_instant_.[[Nanoseconds]], _other_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[LargestUnit]], _settings_.[[RoundingMode]]). + 1. Let _norm_ be DifferenceInstant(_instant_.[[Nanoseconds]], _other_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]]). + 1. Let _result_ be BalanceTimeDuration(_norm_, _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]]). @@ -694,7 +679,8 @@

1. If _duration_.[[Months]] is not 0, throw a *RangeError* exception. 1. If _duration_.[[Weeks]] is not 0, throw a *RangeError* exception. 1. If _duration_.[[Years]] is not 0, throw a *RangeError* exception. - 1. Let _ns_ be ? AddInstant(_instant_.[[Nanoseconds]], _sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). + 1. Let _norm_ be NormalizeTimeDuration(_sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). + 1. Let _ns_ be ? AddInstant(_instant_.[[Nanoseconds]], _norm_). 1. Return ! CreateTemporalInstant(_ns_). diff --git a/spec/intl.html b/spec/intl.html index 76de875274..aabed0bca8 100644 --- a/spec/intl.html +++ b/spec/intl.html @@ -1973,11 +1973,12 @@

Temporal.Calendar.prototype.dateAdd ( _date_, _duration_ [ , _options_ ] ) diff --git a/spec/plaindate.html b/spec/plaindate.html index 27759c1e38..acfbe078fd 100644 --- a/spec/plaindate.html +++ b/spec/plaindate.html @@ -1014,7 +1014,8 @@

1. If _duration_.[[Years]] ≠ 0, or _duration_.[[Months]] ≠ 0, or _duration_.[[Weeks]] ≠ 0, then 1. Return ? CalendarDateAdd(_calendarRec_, _plainDate_, _duration_, _options_). 1. Let _overflow_ be ? ToTemporalOverflow(_options_). - 1. Let _days_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*).[[Days]]. + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Let _days_ be _duration_.[[Days]] + BalanceTimeDuration(_norm_, *"day"*).[[Days]]. 1. Let _result_ be ? AddISODate(_plainDate_.[[ISOYear]], _plainDate_.[[ISOMonth]], _plainDate_.[[ISODay]], 0, 0, 0, _days_, _overflow_). 1. Return ! CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendarRec_.[[Receiver]]). @@ -1066,8 +1067,8 @@

1. Perform ! CreateDataPropertyOrThrow(_resolvedOptions_, *"largestUnit"*, _settings_.[[LargestUnit]]). 1. Let _result_ be ? DifferenceDate(_calendarRec_, _temporalDate_, _other_, _resolvedOptions_). 1. If _roundingGranularityIsNoop_ is *false*, then - 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], 0, 0, 0, 0, 0, 0, _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _temporalDate_, _calendarRec_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. + 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], ZeroTimeDuration(), _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _temporalDate_, _calendarRec_). + 1. Let _roundResult_ be _roundRecord_.[[NormalizedDuration]]. 1. Set _result_ to ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _settings_.[[LargestUnit]], _temporalDate_, _calendarRec_). 1. Return ! CreateTemporalDuration(_sign_ × _result_.[[Years]], _sign_ × _result_.[[Months]], _sign_ × _result_.[[Weeks]], _sign_ × _result_.[[Days]], 0, 0, 0, 0, 0, 0). diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html index 7dfdcf999b..b757e09b46 100644 --- a/spec/plaindatetime.html +++ b/spec/plaindatetime.html @@ -1148,12 +1148,7 @@

_months_: an integer, _weeks_: an integer, _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, _options_: an Object or *undefined*, ): either a normal completion containing an ISO Date-Time Record, or a throw completion

@@ -1165,7 +1160,7 @@

1. Assert: ISODateTimeWithinLimits(_year_, _month_, _day_, _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_) is *true*. - 1. Let _timeResult_ be AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Let _timeResult_ be AddTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _norm_). 1. Let _datePart_ be ! CreateTemporalDate(_year_, _month_, _day_, _calendarRec_.[[Receiver]]). 1. Let _dateDuration_ be ? CreateTemporalDuration(_years_, _months_, _weeks_, _days_ + _timeResult_.[[Days]], 0, 0, 0, 0, 0, 0). 1. Let _addedDate_ be ? AddDate(_calendarRec_, _datePart_, _dateDuration_, _options_). @@ -1248,7 +1243,7 @@

_calendarRec_: a Calendar Methods Record, _largestUnit_: a String, _options_: an Object, - ): either a normal completion containing a Duration Record, or a throw completion + ): either a normal completion containing a Normalized Duration Record, or a throw completion

description
@@ -1262,21 +1257,20 @@

1. Assert: ISODateTimeWithinLimits(_y1_, _mon1_, _d1_, _h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_) is *true*. 1. Assert: ISODateTimeWithinLimits(_y2_, _mon2_, _d2_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_) is *true*. 1. Assert: If _y1_ ≠ _y2_, and _mon1_ ≠ _mon2_, and _d1_ ≠ _d2_, and LargerOfTwoTemporalUnits(_largestUnit_, *"day"*) is not *"day"*, CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateUntil~) is *true*. - 1. Let _timeDifference_ be ! DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). - 1. Let _timeSign_ be ! DurationSign(0, 0, 0, 0, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]]). + 1. Let _timeDuration_ be ! DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + 1. Let _timeSign_ be NormalizedTimeDurationSign(_timeDuration_). 1. Let _dateSign_ be ! CompareISODate(_y2_, _mon2_, _d2_, _y1_, _mon1_, _d1_). 1. Let _adjustedDate_ be CreateISODateRecord(_y1_, _mon1_, _d1_). 1. If _timeSign_ is -_dateSign_, then 1. Set _adjustedDate_ to BalanceISODate(_adjustedDate_.[[Year]], _adjustedDate_.[[Month]], _adjustedDate_.[[Day]] - _timeSign_). - 1. Set _timeDifference_ to ! BalanceTimeDuration(-_timeSign_, _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_). + 1. Set _timeDuration_ to ? Add24HourDaysToNormalizedTimeDuration(_timeDuration_, -_timeSign_). 1. Let _date1_ be ! CreateTemporalDate(_adjustedDate_.[[Year]], _adjustedDate_.[[Month]], _adjustedDate_.[[Day]], _calendarRec_.[[Receiver]]). 1. Let _date2_ be ! CreateTemporalDate(_y2_, _mon2_, _d2_, _calendarRec_.[[Receiver]]). 1. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). 1. Let _untilOptions_ be ? SnapshotOwnProperties(? GetOptionsObject(_options_), *null*). 1. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, _dateLargestUnit_). 1. Let _dateDifference_ be ? DifferenceDate(_calendarRec_, _date1_, _date2_, _untilOptions_). - 1. Let _balanceResult_ be ! BalanceTimeDuration(_dateDifference_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]], _largestUnit_). - 1. Return ! CreateDurationRecord(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]]). + 1. Return CombineDateAndNormalizedTimeDuration(_dateDifference_, _timeDuration_). @@ -1313,15 +1307,19 @@

1. If _datePartsIdentical_ is *false* and _largestUnitIsCalendarUnit_ is *true*, let _largestUnitRequiresDateUntilLookup_ be *true*; else let _largestUnitRequiresDateUntilLookup_ be *false*. 1. If _largestUnitRequiresDateUntilLookup_ is *true*, or _smallestUnitIsCalendarUnit_ is *true*, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateUntil~). - 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]], _calendarRec_, _settings_.[[LargestUnit]], _resolvedOptions_). - 1. If _roundingGranularityIsNoop_ is *true*, 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_, _calendarRec_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. - 1. Let _result_ be ? BalanceTimeDuration(_roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _settings_.[[LargestUnit]]). - 1. Let _balanceResult_ be ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _result_.[[Days]], _settings_.[[LargestUnit]], _relativeTo_, _calendarRec_). - 1. Return ! CreateTemporalDuration(_sign_ × _balanceResult_.[[Years]], _sign_ × _balanceResult_.[[Months]], _sign_ × _balanceResult_.[[Weeks]], _sign_ × _balanceResult_.[[Days]], _sign_ × _result_.[[Hours]], _sign_ × _result_.[[Minutes]], _sign_ × _result_.[[Seconds]], _sign_ × _result_.[[Milliseconds]], _sign_ × _result_.[[Microseconds]], _sign_ × _result_.[[Nanoseconds]]). + 1. Let _result_ 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]], _calendarRec_, _settings_.[[LargestUnit]], _resolvedOptions_). + 1. If _roundingGranularityIsNoop_ is *false*, then + 1. Let _relativeTo_ be ! CreateTemporalDate(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[Calendar]]). + 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[NormalizedTime]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _relativeTo_, _calendarRec_). + 1. Let _roundResult_ be _roundRecord_.[[NormalizedDuration]]. + 1. Let _normWithDays_ be ? Add24HourDaysToNormalizedTimeDuration(_roundResult_.[[NormalizedTime]], _roundResult_.[[Days]]). + 1. Let _timeResult_ be BalanceTimeDuration(_normWithDays_, _settings_.[[LargestUnit]]). + 1. Let _balanceResult_ be ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _timeResult_.[[Days]], _settings_.[[LargestUnit]], _relativeTo_, _calendarRec_). + 1. Else, + 1. Let _normWithDays_ be ? Add24HourDaysToNormalizedTimeDuration(_result_.[[NormalizedTime]], _result_.[[Days]]). + 1. Let _timeResult_ be BalanceTimeDuration(_normWithDays_, _settings_.[[LargestUnit]]). + 1. Let _balanceResult_ be ! CreateDateDurationRecord(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _timeResult_.[[Days]]). + 1. Return ! CreateTemporalDuration(_sign_ × _balanceResult_.[[Years]], _sign_ × _balanceResult_.[[Months]], _sign_ × _balanceResult_.[[Weeks]], _sign_ × _balanceResult_.[[Days]], _sign_ × _timeResult_.[[Hours]], _sign_ × _timeResult_.[[Minutes]], _sign_ × _timeResult_.[[Seconds]], _sign_ × _timeResult_.[[Milliseconds]], _sign_ × _timeResult_.[[Microseconds]], _sign_ × _timeResult_.[[Nanoseconds]]). @@ -1344,7 +1342,8 @@

1. Let _calendarRec_ be ! CreateCalendarMethodsRecord(_dateTime_.[[Calendar]], « »). 1. If _duration_.[[Years]] ≠ 0, or _duration_.[[Months]] ≠ 0, or _duration_.[[Weeks]] ≠ 0, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateAdd~). - 1. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _calendarRec_, _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 _norm_ be NormalizeTimeDuration(_sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). + 1. Let _result_ be ? AddDateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _calendarRec_, _sign_ × _duration_.[[Years]], _sign_ × _duration_.[[Months]], _sign_ × _duration_.[[Weeks]], _sign_ × _duration_.[[Days]], _norm_, _options_). 1. Assert: IsValidISODate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]]) is *true*. 1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*. 1. Return ? CreateTemporalDateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _dateTime_.[[Calendar]]). diff --git a/spec/plaintime.html b/spec/plaintime.html index a09154b985..73d94fe743 100644 --- a/spec/plaintime.html +++ b/spec/plaintime.html @@ -600,7 +600,7 @@

description
-
It returns a Time Duration Record with the elapsed duration from a first wall-clock time, until a second wall-clock time.
+
It returns a Normalized Time Duration Record with the elapsed duration from a first wall-clock time, until a second wall-clock time.
1. Let _hours_ be _h2_ - _h1_. @@ -609,10 +609,9 @@

1. Let _milliseconds_ be _ms2_ - _ms1_. 1. Let _microseconds_ be _mus2_ - _mus1_. 1. Let _nanoseconds_ be _ns2_ - _ns1_. - 1. Let _sign_ be ! DurationSign(0, 0, 0, 0, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). - 1. Let _bt_ be BalanceTime(_hours_ × _sign_, _minutes_ × _sign_, _seconds_ × _sign_, _milliseconds_ × _sign_, _microseconds_ × _sign_, _nanoseconds_ × _sign_). - 1. Assert: _bt_.[[Days]] is 0. - 1. Return ! CreateTimeDurationRecord(0, _bt_.[[Hour]] × _sign_, _bt_.[[Minute]] × _sign_, _bt_.[[Second]] × _sign_, _bt_.[[Millisecond]] × _sign_, _bt_.[[Microsecond]] × _sign_, _bt_.[[Nanosecond]] × _sign_). + 1. Let _norm_ be NormalizeTimeDuration(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Assert: NormalizedTimeDurationAbs(_norm_).[[TotalNanoseconds]] < nsPerDay. + 1. Return _norm_. @@ -916,12 +915,7 @@

_millisecond_: an integer in the inclusive range 0 to 999, _microsecond_: an integer in the inclusive range 0 to 999, _nanosecond_: an integer in the inclusive range 0 to 999, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, ): a Time Record

@@ -929,12 +923,8 @@

- 1. Set _hour_ to _hour_ + _hours_. - 1. Set _minute_ to _minute_ + _minutes_. - 1. Set _second_ to _second_ + _seconds_. - 1. Set _millisecond_ to _millisecond_ + _milliseconds_. - 1. Set _microsecond_ to _microsecond_ + _microseconds_. - 1. Set _nanosecond_ to _nanosecond_ + _nanoseconds_. + 1. Set _second_ to _second_ + NormalizedTimeDurationSeconds(_norm_). + 1. Set _nanosecond_ to _nanosecond_ + NormalizedTimeDurationSubseconds(_norm_). 1. Return BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
@@ -1019,11 +1009,11 @@

1. Set _other_ to ? ToTemporalTime(_other_). 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 _norm_ 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. 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. Let _roundRecord_ be ! RoundDuration(0, 0, 0, 0, _norm_, _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]]). + 1. Set _norm_ to _roundRecord_.[[NormalizedDuration]].[[NormalizedTime]]. + 1. Let _result_ be BalanceTimeDuration(_norm_, _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]]). @@ -1042,8 +1032,8 @@

1. If _operation_ is ~subtract~, let _sign_ be -1. Otherwise, let _sign_ be 1. 1. Let _duration_ be ? ToTemporalDurationRecord(_temporalDurationLike_). - 1. Let _result_ be AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). - 1. Assert: IsValidTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]) is *true*. + 1. Let _norm_ be NormalizeTimeDuration(_sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). + 1. Let _result_ be AddTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _norm_). 1. Return ! CreateTemporalTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]). diff --git a/spec/plainyearmonth.html b/spec/plainyearmonth.html index 3b4d256d4c..920e28cb84 100644 --- a/spec/plainyearmonth.html +++ b/spec/plainyearmonth.html @@ -657,8 +657,8 @@

1. Perform ! CreateDataPropertyOrThrow(_resolvedOptions_, *"largestUnit"*, _settings_.[[LargestUnit]]). 1. Let _result_ be ? CalendarDateUntil(_calendarRec_, _thisDate_, _otherDate_, _resolvedOptions_). 1. If _settings_.[[SmallestUnit]] is not *"month"* or _settings_.[[RoundingIncrement]] ≠ 1, then - 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _thisDate_, _calendarRec_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. + 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], 0, 0, ZeroTimeDuration(), _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _thisDate_, _calendarRec_). + 1. Let _roundResult_ be _roundRecord_.[[NormalizedDuration]]. 1. Set _result_ to ? BalanceDateDurationRelative(_roundResult_.[[Years]], _roundResult_.[[Months]], 0, 0, _settings_.[[LargestUnit]], _thisDate_, _calendarRec_). 1. Return ! CreateTemporalDuration(_sign_ × _result_.[[Years]], _sign_ × _result_.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). @@ -680,8 +680,10 @@

1. Let _duration_ be ? ToTemporalDuration(_temporalDurationLike_). 1. If _operation_ is ~subtract~, then 1. Set _duration_ to ! CreateNegatedTemporalDuration(_duration_). - 1. Let _balanceResult_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*). - 1. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0). + 1. Let _norm_ be NormalizeTimeDuration(_duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]]). + 1. Let _balanceResult_ be BalanceTimeDuration(_norm_, *"day"*). + 1. Let _days_ be _duration_.[[Days]] + _balanceResult_.[[Days]]. + 1. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _days_, 0, 0, 0, 0, 0, 0). 1. Let _calendarRec_ be ! CreateCalendarMethodsRecord(_yearMonth_.[[Calendar]], « »). 1. If _sign_ < 0, or _duration_.[[Years]] ≠ 0, or _duration_.[[Months]] ≠ 0, or _duration_.[[Weeks]] ≠ 0, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateAdd~). @@ -704,7 +706,7 @@

1. Let _date_ be ? CalendarDateFromFields(_calendarRec_, _fieldsCopy_). 1. Else, 1. Let _date_ be _intermediateDate_. - 1. Let _durationToAdd_ be ! CreateTemporalDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0). + 1. Let _durationToAdd_ be ! CreateTemporalDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _days_, 0, 0, 0, 0, 0, 0). 1. Set _options_ to ? GetOptionsObject(_options_). 1. Let _optionsCopy_ be ? SnapshotOwnProperties(_options_, *null*). 1. Let _addedDate_ be ? AddDate(_calendarRec_, _date_, _durationToAdd_, _options_). diff --git a/spec/timezone.html b/spec/timezone.html index 4f47775121..4141fae361 100644 --- a/spec/timezone.html +++ b/spec/timezone.html @@ -942,14 +942,16 @@

1. Let _offsetAfter_ be ? GetOffsetNanosecondsFor(_timeZoneRec_, _dayAfter_). 1. Let _nanoseconds_ be _offsetAfter_ - _offsetBefore_. 1. If _disambiguation_ is *"earlier"*, then - 1. Let _earlierTime_ be AddTime(_dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, -_nanoseconds_). + 1. Let _norm_ be NormalizeTimeDuration(0, 0, 0, 0, 0, -_nanoseconds_). + 1. Let _earlierTime_ be AddTime(_dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _norm_). 1. Let _earlierDate_ be AddISODate(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], 0, 0, 0, _earlierTime_.[[Days]], *"constrain"*). 1. Let _earlierDateTime_ be ! CreateTemporalDateTime(_earlierDate_.[[Year]], _earlierDate_.[[Month]], _earlierDate_.[[Day]], _earlierTime_.[[Hour]], _earlierTime_.[[Minute]], _earlierTime_.[[Second]], _earlierTime_.[[Millisecond]], _earlierTime_.[[Microsecond]], _earlierTime_.[[Nanosecond]], *"iso8601"*). 1. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZoneRec_, _earlierDateTime_). 1. If _possibleInstants_ is empty, throw a *RangeError* exception. 1. Return _possibleInstants_[0]. 1. Assert: _disambiguation_ is *"compatible"* or *"later"*. - 1. Let _laterTime_ be AddTime(_dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], 0, 0, 0, 0, 0, _nanoseconds_). + 1. Let _norm_ be NormalizeTimeDuration(0, 0, 0, 0, 0, _nanoseconds_). + 1. Let _laterTime_ be AddTime(_dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _norm_). 1. Let _laterDate_ be AddISODate(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], 0, 0, 0, _laterTime_.[[Days]], *"constrain"*). 1. Let _laterDateTime_ be ! CreateTemporalDateTime(_laterDate_.[[Year]], _laterDate_.[[Month]], _laterDate_.[[Day]], _laterTime_.[[Hour]], _laterTime_.[[Minute]], _laterTime_.[[Second]], _laterTime_.[[Millisecond]], _laterTime_.[[Microsecond]], _laterTime_.[[Nanosecond]], *"iso8601"*). 1. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZoneRec_, _laterDateTime_). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index 22b6edb779..883609c15d 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -1300,12 +1300,7 @@

_months_: an integer, _weeks_: an integer, _days_: an integer, - _hours_: an integer, - _minutes_: an integer, - _seconds_: an integer, - _milliseconds_: an integer, - _microseconds_: an integer, - _nanoseconds_: an integer, + _norm_: a Normalized Time Duration Record, optional _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, optional _options_: an Object, ): either a normal completion containing a BigInt or an abrupt completion @@ -1325,7 +1320,7 @@

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. Return ? AddInstant(_epochNanoseconds_, _norm_). 1. Let _instant_ be ! CreateTemporalInstant(_epochNanoseconds_). 1. If _precalculatedPlainDateTime_ is not *undefined*, then 1. Let _temporalDateTime_ be _precalculatedPlainDateTime_. @@ -1334,14 +1329,14 @@

1. If _years_ = 0, and _months_ = 0, and _weeks_ = 0, then 1. Let _overflow_ be ? ToTemporalOverflow(_options_). 1. Let _intermediate_ be ? AddDaysToZonedDateTime(_instant_, _temporalDateTime_, _timeZoneRec_, _calendarRec_.[[Receiver]], _days_, _overflow_).[[EpochNanoseconds]]. - 1. Return ? AddInstant(_intermediate_, _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Return ? AddInstant(_intermediate_, _norm_). 1. Assert: CalendarMethodsRecordHasLookedUp(_calendarRec_, ~dateAdd~) is *true*. 1. Let _datePart_ be ! CreateTemporalDate(_temporalDateTime_.[[ISOYear]], _temporalDateTime_.[[ISOMonth]], _temporalDateTime_.[[ISODay]], _calendarRec_.[[Receiver]]). 1. Let _dateDuration_ be ! CreateTemporalDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). 1. Let _addedDate_ be ? CalendarDateAdd(_calendarRec_, _datePart_, _dateDuration_, _options_). 1. Let _intermediateDateTime_ be ? CreateTemporalDateTime(_addedDate_.[[ISOYear]], _addedDate_.[[ISOMonth]], _addedDate_.[[ISODay]], _temporalDateTime_.[[ISOHour]], _temporalDateTime_.[[ISOMinute]], _temporalDateTime_.[[ISOSecond]], _temporalDateTime_.[[ISOMillisecond]], _temporalDateTime_.[[ISOMicrosecond]], _temporalDateTime_.[[ISONanosecond]], _calendarRec_.[[Receiver]]). 1. Let _intermediateInstant_ be ? GetInstantFor(_timeZoneRec_, _intermediateDateTime_, *"compatible"*). - 1. Return ? AddInstant(_intermediateInstant_.[[Nanoseconds]], _hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + 1. Return ? AddInstant(_intermediateInstant_.[[Nanoseconds]], _norm_). @@ -1395,7 +1390,7 @@

_largestUnit_: a String, _options_: an Object, _precalculatedPlainDateTime_: a Temporal.PlainDateTime or *undefined*, - ): either a normal completion containing a Duration Record or an abrupt completion + ): either a normal completion containing a Normalized Duration Record, or a throw completion

description
@@ -1405,7 +1400,7 @@

1. If _ns1_ is _ns2_, then - 1. Return ! CreateDurationRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0). + 1. Return ! CreateNormalizedDurationRecord(0, 0, 0, 0, ZeroTimeDuration()). 1. If _precalculatedPlainDateTime_ is *undefined*, then 1. Let _startInstant_ be ! CreateTemporalInstant(_ns1_). 1. Let _startDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, _calendarRec_.[[Receiver]]). @@ -1414,39 +1409,38 @@

1. Let _endInstant_ be ! CreateTemporalInstant(_ns2_). 1. Let _endDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _endInstant_, _calendarRec_.[[Receiver]]). 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]], _calendarRec_, _largestUnit_, _options_). - 1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZoneRec_, _calendarRec_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0, _startDateTime_). - 1. Let _timeRemainderNs_ be _ns2_ - _intermediateNs_. + 1. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZoneRec_, _calendarRec_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, ZeroTimeDuration(), _startDateTime_). + 1. Let _norm_ be NormalizedTimeDurationFromEpochNanosecondsDifference(_ns2_, _intermediateNs_). 1. Let _intermediate_ be ! CreateTemporalZonedDateTime(_intermediateNs_, _timeZoneRec_.[[Receiver]], _calendarRec_.[[Receiver]]). - 1. Let _result_ be ? NanosecondsToDays(ℝ(_timeRemainderNs_), _intermediate_, _timeZoneRec_). - 1. Let _timeDifference_ be ! BalanceTimeDuration(0, 0, 0, 0, 0, 0, _result_.[[Nanoseconds]], *"hour"*). - 1. Return ! CreateDurationRecord(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _result_.[[Days]], _timeDifference_.[[Hours]], _timeDifference_.[[Minutes]], _timeDifference_.[[Seconds]], _timeDifference_.[[Milliseconds]], _timeDifference_.[[Microseconds]], _timeDifference_.[[Nanoseconds]]). + 1. Let _result_ be ? NormalizedTimeDurationToDays(_norm_, _intermediate_, _timeZoneRec_). + 1. Return ! CreateNormalizedDurationRecord(_dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], _result_.[[Days]], _result_.[[Remainder]]). - +

- NanosecondsToDays ( - _nanoseconds_: an integer, + NormalizedTimeDurationToDays ( + _norm_: a Normalized Time Duration Record, _zonedRelativeTo_: a Temporal.ZonedDateTime, _timeZoneRec_: a Time Zone Methods Record, optional _precalculatedPlainDateTime_: a Temporal.PlainDateTime, - ): either a normal completion containing a Record with fields [[Days]] (an integer), [[Nanoseconds]] (an integer), and [[DayLength]] (an integer), or an abrupt completion + ): either a normal completion containing a Record with fields [[Days]] (an integer), [[Remainder]] (a Normalized Time Duration Record), and [[DayLength]] (an integer), or an abrupt completion

description
- 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 converts a normalized time duration _norm_ relative to a Temporal.ZonedDateTime _zonedRelativeTo_, and converts it into a number of days and a remainder normalized time duration, 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.
- 1. If _nanoseconds_ = 0, then - 1. Return the Record { [[Days]]: 0, [[Nanoseconds]]: 0, [[DayLength]]: nsPerDay }. - 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 _endNs_ be _startNs_ + _nanoseconds_. - 1. If IsValidEpochNanoseconds(ℤ(_endNs_)) is *false*, throw a *RangeError* exception. + 1. Let _sign_ be NormalizedTimeDurationSign(_norm_). + 1. If _sign_ = 0, then + 1. Return the Record { [[Days]]: 0, [[Remainder]]: _norm_, [[DayLength]]: nsPerDay }. + 1. Let _startNs_ be _zonedRelativeTo_.[[Nanoseconds]]. + 1. Let _startInstant_ be ! CreateTemporalInstant(_startNs_). + 1. Let _endNs_ be AddNormalizedTimeDurationToEpochNanoseconds(_norm_, _startNs_). + 1. If ! IsValidEpochNanoseconds(_endNs_) is *false*, throw a *RangeError* exception. 1. Let _endInstant_ be ! CreateTemporalInstant(ℤ(_endNs_)). 1. If _precalculatedPlainDateTime_ is present, let _startDateTime_ be _precalculatedPlainDateTime_; else let _startDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _startInstant_, *"iso8601"*). 1. Let _endDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _endInstant_, *"iso8601"*). @@ -1463,28 +1457,29 @@

1. Repeat, while _days_ > 0 and ℝ(_relativeResult_.[[EpochNanoseconds]]) > _endNs_, 1. Set _days_ to _days_ - 1. 1. Set _relativeResult_ to ? AddDaysToZonedDateTime(_startInstant_, _startDateTime_, _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _days_). - 1. Set _nanoseconds_ to _endNs_ - ℝ(_relativeResult_.[[EpochNanoseconds]]). + 1. Set _norm_ to NormalizedTimeDurationFromEpochNanosecondsDifference(_endNs_, _relativeResult_.[[EpochNanoseconds]]). 1. Let _done_ be *false*. 1. Let _dayLengthNs_ be ~unset~. 1. Repeat, while _done_ is *false*, 1. Let _oneDayFarther_ be ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_). - 1. Set _dayLengthNs_ to ℝ(_oneDayFarther_.[[EpochNanoseconds]] - _relativeResult_.[[EpochNanoseconds]]). - 1. If (_nanoseconds_ - _dayLengthNs_) × _sign_ ≥ 0, then - 1. Set _nanoseconds_ to _nanoseconds_ - _dayLengthNs_. + 1. Set _dayLengthNs_ to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther_.[[EpochNanoseconds]], _relativeResult_.[[EpochNanoseconds]]). + 1. Let _oneDayLess_ be ? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_). + 1. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then + 1. Set _norm_ to _oneDayLess_. 1. Set _relativeResult_ to _oneDayFarther_. 1. Set _days_ to _days_ + _sign_. 1. Else, 1. Set _done_ to *true*. 1. If _days_ < 0 and _sign_ = 1, throw a *RangeError* exception. 1. If _days_ > 0 and _sign_ = -1, throw a *RangeError* exception. - 1. If _nanoseconds_ < 0, then + 1. If NormalizedTimeDurationSign(_norm_) = -1, then 1. Assert: _sign_ is -1. - 1. If _nanoseconds_ > 0 and _sign_ = -1, throw a *RangeError* exception. - 1. Assert: The inequality abs(_nanoseconds_) < abs(_dayLengthNs_) holds. + 1. If NormalizedTimeDurationSign(_norm_) = 1 and _sign_ = -1, throw a *RangeError* exception. + 1. Assert: CompareNormalizedTimeDuration(NormalizedTimeDurationAbs(_norm_), NormalizedTimeDurationAbs(_dayLengthNs_)) = -1. 1. Return the Record { [[Days]]: _days_, - [[Nanoseconds]]: _nanoseconds_, - [[DayLength]]: abs(_dayLengthNs_) + [[Remainder]]: _norm_, + [[DayLength]]: ℝ(NormalizedTimeDurationAbs(_dayLengthNs_).[[TotalNanoseconds]]) }. @@ -1509,7 +1504,8 @@

1. Let _resolvedOptions_ be ? SnapshotOwnProperties(? GetOptionsObject(_options_), *null*). 1. Let _settings_ be ? GetDifferenceSettings(_operation_, _resolvedOptions_, ~datetime~, « », *"nanosecond"*, *"hour"*). 1. If _settings_.[[LargestUnit]] is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then - 1. Let _result_ be DifferenceInstant(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[LargestUnit]], _settings_.[[RoundingMode]]). + 1. Let _norm_ be DifferenceInstant(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]]). + 1. Let _result_ be BalanceTimeDuration(_norm_, _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]]). 1. If ? TimeZoneEquals(_zonedDateTime_.[[TimeZone]], _other_.[[TimeZone]]) is *false*, then 1. Throw a *RangeError* exception. @@ -1521,15 +1517,18 @@

1. Let _precalculatedPlainDateTime_ be ? GetPlainDateTimeFor(_timeZoneRec_, _instant_, _calendarRec_.[[Receiver]]). 1. Let _plainRelativeTo_ be ! CreateTemporalDate(_precalculatedPlainDateTime_.[[ISOYear]], _precalculatedPlainDateTime_.[[ISOMonth]], _precalculatedPlainDateTime_.[[ISODay]], _calendarRec_.[[Receiver]]). 1. Perform ! CreateDataPropertyOrThrow(_resolvedOptions_, *"largestUnit"*, _settings_.[[LargestUnit]]). - 1. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _settings_.[[LargestUnit]], _resolvedOptions_, _precalculatedPlainDateTime_). + 1. Let _result_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _settings_.[[LargestUnit]], _resolvedOptions_, _precalculatedPlainDateTime_). 1. If _settings_.[[SmallestUnit]] is *"nanosecond"* and _settings_.[[RoundingIncrement]] is 1, let _roundingGranularityIsNoop_ be *true*; else let _roundingGranularityIsNoop_ be *false*. - 1. If _roundingGranularityIsNoop_ is *true*, 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]], _plainRelativeTo_, _calendarRec_, _zonedDateTime_, _timeZoneRec_, _precalculatedPlainDateTime_). - 1. Let _roundResult_ be _roundRecord_.[[DurationRecord]]. - 1. Let _adjustResult_ 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_, _calendarRec_, _timeZoneRec_, _precalculatedPlainDateTime_). - 1. Let _result_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _plainRelativeTo_, _calendarRec_). - 1. Return ! CreateTemporalDuration(_sign_ × _result_.[[Years]], _sign_ × _result_.[[Months]], _sign_ × _result_.[[Weeks]], _sign_ × _result_.[[Days]], _sign_ × _adjustResult_.[[Hours]], _sign_ × _adjustResult_.[[Minutes]], _sign_ × _adjustResult_.[[Seconds]], _sign_ × _adjustResult_.[[Milliseconds]], _sign_ × _adjustResult_.[[Microseconds]], _sign_ × _adjustResult_.[[Nanoseconds]]). + 1. If _roundingGranularityIsNoop_ is *false*, then + 1. Let _roundRecord_ be ? RoundDuration(_result_.[[Years]], _result_.[[Months]], _result_.[[Weeks]], _result_.[[Days]], _result_.[[NormalizedTime]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _plainRelativeTo_, _calendarRec_, _zonedDateTime_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _roundResult_ be _roundRecord_.[[NormalizedDuration]]. + 1. Let _daysResult_ be ! NormalizedTimeDurationToDays(_roundResult_.[[NormalizedTime]], _zonedDateTime_, _timeZoneRec_). + 1. Let _days_ be _roundResult_.[[Days]] + _daysResult_.[[Days]]. + 1. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _days_, _daysResult_.[[NormalizedTime]], _settings_.[[RoundingIncrement]], _settings_.[[SmallestUnit]], _settings_.[[RoundingMode]], _zonedDateTime_, _calendarRec_, _timeZoneRec_, _precalculatedPlainDateTime_). + 1. Let _balanceResult_ be ? BalanceDateDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _settings_.[[LargestUnit]], _plainRelativeTo_, _calendarRec_). + 1. Set _result_ to CombineDateAndNormalizedTimeDuration(_balanceResult_, _adjustResult_.[[NormalizedTime]]). + 1. Let _timeResult_ be BalanceTimeDuration(_result_.[[NormalizedTime]], *"hour"*). + 1. Return ! CreateTemporalDuration(_sign_ × _result_.[[Years]], _sign_ × _result_.[[Months]], _sign_ × _result_.[[Weeks]], _sign_ × _result_.[[Days]], _sign_ × _timeResult_.[[Hours]], _sign_ × _timeResult_.[[Minutes]], _sign_ × _timeResult_.[[Seconds]], _sign_ × _timeResult_.[[Milliseconds]], _sign_ × _timeResult_.[[Microseconds]], _sign_ × _timeResult_.[[Nanoseconds]]). @@ -1553,7 +1552,8 @@

1. Let _calendarRec_ be ? CreateCalendarMethodsRecord(_zonedDateTime_.[[Calendar]], « »). 1. If _duration_.[[Years]] ≠ 0, or _duration_.[[Months]] ≠ 0, or _duration_.[[Weeks]] ≠ 0, then 1. Perform ? CalendarMethodsRecordLookup(_calendarRec_, ~dateAdd~). - 1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _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. Let _norm_ be NormalizeTimeDuration(_sign_ × _duration_.[[Hours]], _sign_ × _duration_.[[Minutes]], _sign_ × _duration_.[[Seconds]], _sign_ × _duration_.[[Milliseconds]], _sign_ × _duration_.[[Microseconds]], _sign_ × _duration_.[[Nanoseconds]]). + 1. Let _epochNanoseconds_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _timeZoneRec_, _calendarRec_, _sign_ × _duration_.[[Years]], _sign_ × _duration_.[[Months]], _sign_ × _duration_.[[Weeks]], _sign_ × _duration_.[[Days]], _norm_, *undefined*, _options_). 1. Return ! CreateTemporalZonedDateTime(_epochNanoseconds_, _timeZoneRec_.[[Receiver]], _calendarRec_.[[Receiver]]).