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_ ] )
1. Set _duration_ to ? ToTemporalDuration(_duration_).
1. Set _options_ to ? GetOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
- 1. Let _balanceResult_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*).
- 1. Let _result_ be ? AddISODate(_date_.[[ISOYear]], _date_.[[ISOMonth]], _date_.[[ISODay]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], _overflow_).
+ 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 _result_ be ? AddISODate(_date_.[[ISOYear]], _date_.[[ISOMonth]], _date_.[[ISODay]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]] + _balanceResult_.[[Days]], _overflow_).
1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], *"iso8601"*).
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 Name |
+ Value |
+ Meaning |
+
+
+ [[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 Name |
+ Value |
+ Meaning |
+
+
+ [[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
- 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
+
+
+
+ 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
+
+
+
+ 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
@@ -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
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 1. Return _epochNs_ + ℤ(_d_.[[TotalNanoseconds]]).
+
+
+
+
+
+ CompareNormalizedTimeDuration (
+ _one_: a Normalized Time Duration Record,
+ _two_: a Normalized Time Duration Record,
+ ): -1, 0, or 1
+
+
+
+ 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
+
+
+
+ 1. Assert: _divisor_ ≠ 0.
+ 1. Return _d_.[[TotalNanoseconds]] / _divisor_.
+
+
+
+
+
+ NormalizedTimeDurationFromEpochNanosecondsDifference (
+ _one_: a BigInt,
+ _two_: a BigInt,
+ ): a Normalized Time Duration Record
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 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*𝔽
+
+
+
+ 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
+
+
+
+ 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
+
+
+
+ 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
- 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
@@ -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
- 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,
)
- 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
- 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_ ] )
1. Set _duration_ to ? ToTemporalDuration(_duration_).
1. Set _options_ to ? GetOptionsObject(_options_).
1. Let _overflow_ be ? ToTemporalOverflow(_options_).
- 1. Let _balanceResult_ be ? BalanceTimeDuration(_duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], *"day"*).
+ 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. If _calendar_.[[Identifier]] is *"iso8601"*, then
- 1. Let _result_ be ? AddISODate(_date_.[[ISOYear]], _date_.[[ISOMonth]], _date_.[[ISODay]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], _overflow_).
+ 1. Let _result_ be ? AddISODate(_date_.[[ISOYear]], _date_.[[ISOMonth]], _date_.[[ISODay]], _duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]] + _balanceResult_.[[Days]], _overflow_).
1. Else,
- 1. Let _balancedDuration_ be ! CreateDateDurationRecord(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]]).
+ 1. Let _balancedDuration_ be ! CreateDateDurationRecord(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]] + _balanceResult_.[[Days]]).
1. Let _result_ be ? CalendarDateAddition(_calendar_.[[Identifier]], _date_, _balancedDuration_, _overflow_).
1. Return ? CreateTemporalDate(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _calendar_.[[Identifier]]).
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
@@ -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 @@
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
- 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
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
- 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]]).