Temporal: Avoid calendar operations when adding days-only duration to ZDT

This allows removing several tests, as calendar.dateAdd() is no longer
called in several places, and it's no longer possible to create a
situation where BigInt arithmetic is observable in NanosecondsToDays.
This commit is contained in:
Philip Chimento 2023-03-08 16:11:55 -08:00 committed by Philip Chimento
parent d2a6e21ff9
commit 8040379076
18 changed files with 47 additions and 308 deletions

View File

@ -14,4 +14,4 @@ const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
const instance = new Temporal.Duration(1, 1, 1, 1);
instance.add(instance, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
assert.sameValue(calendar.dateAddCallCount, 5);
assert.sameValue(calendar.dateAddCallCount, 3);

View File

@ -262,15 +262,9 @@ const expectedOpsForZonedRelativeTo = expected.concat([
"get options.relativeTo.calendar.dateUntil",
"call options.relativeTo.calendar.dateUntil",
// AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1
"get options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
]);

View File

@ -19,17 +19,15 @@ const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
// Duration.round() ->
// RoundDuration ->
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// BalanceDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
// BalanceDurationRelative ->
// MoveRelativeDate -> calendar.dateAdd() (2x)
// calendar.dateAdd()
const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
instance1.round({ smallestUnit: "days", relativeTo });
assert.sameValue(calendar.dateAddCallCount, 8, "rounding with calendar smallestUnit");
assert.sameValue(calendar.dateAddCallCount, 5, "rounding with calendar smallestUnit");
// Rounding with a non-default largestUnit to cover the path in
// UnbalanceDurationRelative where larger units are converted into smaller
@ -55,17 +53,13 @@ assert.sameValue(calendar.dateAddCallCount, 8, "rounding with non-default larges
// AdjustRoundedDurationDays.
// The calls come from these paths:
// Duration.round() ->
// AdjustRoundedDurationDays ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// AddDuration ->
// AdjustRoundedDurationDays -> AddDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
// BalanceDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
calendar.dateAddCallCount = 0;
const instance3 = new Temporal.Duration(0, 0, 0, 0, 23, 59, 59, 999, 999, 999);
instance3.round({ largestUnit: "days", smallestUnit: "hours", roundingMode: "ceil", relativeTo });
assert.sameValue(calendar.dateAddCallCount, 7, "rounding with time difference exceeding calendar day");
assert.sameValue(calendar.dateAddCallCount, 2, "rounding with time difference exceeding calendar day");

View File

@ -1,77 +0,0 @@
// Copyright (C) 2022 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.round
description: >
NanosecondsToDays computes with precise mathematical integers.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
14. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. ...
Ensure |days = days - 1| is exact and doesn't loose precision.
features: [Temporal]
---*/
var expectedDurationDays = [
Number.MAX_SAFE_INTEGER + 4, // 9007199254740996
Number.MAX_SAFE_INTEGER + 3, // 9007199254740994
Number.MAX_SAFE_INTEGER + 2, // 9007199254740992
Number.MAX_SAFE_INTEGER + 1, // 9007199254740992
Number.MAX_SAFE_INTEGER + 0, // 9007199254740991
Number.MAX_SAFE_INTEGER - 1, // 9007199254740990
Number.MAX_SAFE_INTEGER - 2, // 9007199254740989
Number.MAX_SAFE_INTEGER - 3, // 9007199254740988
Number.MAX_SAFE_INTEGER - 4, // 9007199254740987
Number.MAX_SAFE_INTEGER - 5, // 9007199254740986
];
// Intentionally not Test262Error to ensure assertions errors are propagated.
class StopExecution extends Error {}
var cal = new class extends Temporal.Calendar {
#dateUntil = 0;
dateUntil(one, two, options) {
if (++this.#dateUntil === 1) {
return Temporal.Duration.from({days: Number.MAX_SAFE_INTEGER + 4});
}
return super.dateUntil(one, two, options);
}
#dateAdd = 0;
dateAdd(date, duration, options) {
// Ensure we don't add too many days which would lead to creating an invalid date.
if (++this.#dateAdd === 3) {
assert.sameValue(duration.days, Number.MAX_SAFE_INTEGER + 4);
// The added days must be larger than 5 for the |intermediateNs > endNs| condition.
return super.dateAdd(date, "P6D", options);
}
// Ensure the duration days are exact.
if (this.#dateAdd > 3) {
if (!expectedDurationDays.length) {
throw new StopExecution();
}
assert.sameValue(duration.days, expectedDurationDays.shift());
// Add more than 5 for the |intermediateNs > endNs| condition.
return super.dateAdd(date, "P6D", options);
}
// Otherwise call the default implementation.
return super.dateAdd(date, duration, options);
}
}("iso8601");
var zoned = new Temporal.ZonedDateTime(0n, "UTC", cal);
var duration = Temporal.Duration.from({days: 5});
var options = {smallestUnit: "days", relativeTo: zoned};
assert.throws(StopExecution, () => duration.round(options));

View File

@ -1,45 +0,0 @@
// Copyright (C) 2022 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.round
description: >
NanosecondsToDays computes with precise mathematical integers.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
17. Repeat, while done is false,
...
c. If (nanoseconds - dayLengthNs) × sign 0, then
...
iii. Set days to days + sign.
Ensure |days = days + sign| is exact and doesn't loose precision.
features: [Temporal]
---*/
var cal = new class extends Temporal.Calendar {
#dateUntil = 0;
dateUntil(one, two, options) {
if (++this.#dateUntil === 1) {
return Temporal.Duration.from({days: Number.MAX_SAFE_INTEGER + 10});
}
return super.dateUntil(one, two, options);
}
#dateAdd = 0;
dateAdd(date, duration, options) {
if (++this.#dateAdd === 3) {
return super.dateAdd(date, "P1D", options);
}
return super.dateAdd(date, duration, options);
}
}("iso8601");
var zoned = new Temporal.ZonedDateTime(0n, "UTC", cal);
var duration = Temporal.Duration.from({days: 5});
var result = duration.round({smallestUnit: "days", relativeTo: zoned});
assert.sameValue(result.days, Number(BigInt(Number.MAX_SAFE_INTEGER + 10) + 5n));

View File

@ -362,17 +362,11 @@ const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateUntil", // 12. DifferenceISODateTime
"call options.relativeTo.calendar.dateUntil",
// NanosecondsToDays → AddZonedDateTime
"get options.relativeTo.calendar.dateAdd", // 8.
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
// NanosecondsToDays → AddDaysToZonedDateTime
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// NanosecondsToDays → AddZonedDateTime
"get options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5. GetPlainDateTimeFor
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateAdd", // 8.
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
// NanosecondsToDays → AddDaysToZonedDateTime
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"get options.relativeTo.calendar.dateAdd", // 9.b
"call options.relativeTo.calendar.dateAdd", // 9.c

View File

@ -14,4 +14,4 @@ const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
const instance = new Temporal.Duration(1, 1, 1, 1);
instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) });
assert.sameValue(calendar.dateAddCallCount, 5);
assert.sameValue(calendar.dateAddCallCount, 3);

View File

@ -262,15 +262,9 @@ const expectedOpsForZonedRelativeTo = expected.concat([
"get options.relativeTo.calendar.dateUntil",
"call options.relativeTo.calendar.dateUntil",
// AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1
"get options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
]);

View File

@ -21,14 +21,12 @@ const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
// UnbalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd() (3x)
// BalanceDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
// RoundDuration ->
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
instance1.total({ unit: "days", relativeTo });
assert.sameValue(calendar.dateAddCallCount, 8, "converting larger calendar units down");
assert.sameValue(calendar.dateAddCallCount, 5, "converting larger calendar units down");
// Total of a calendar unit where smaller calendar units have to be converted
// up, to cover the path that goes through MoveRelativeZonedDateTime
@ -37,7 +35,6 @@ assert.sameValue(calendar.dateAddCallCount, 8, "converting larger calendar units
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// BalanceDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
// RoundDuration ->
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// MoveRelativeDate -> calendar.dateAdd()
@ -46,4 +43,4 @@ calendar.dateAddCallCount = 0;
const instance2 = new Temporal.Duration(0, 0, 1, 1);
instance2.total({ unit: "weeks", relativeTo });
assert.sameValue(calendar.dateAddCallCount, 6, "converting smaller calendar units up");
assert.sameValue(calendar.dateAddCallCount, 4, "converting smaller calendar units up");

View File

@ -269,17 +269,11 @@ const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateUntil", // 12. DifferenceISODateTime
"call options.relativeTo.calendar.dateUntil",
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddZonedDateTime
"get options.relativeTo.calendar.dateAdd", // 8.
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddZonedDateTime
"get options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5. GetPlainDateTimeFor
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.calendar.dateAdd", // 8.
"call options.relativeTo.calendar.dateAdd",
"get options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
], [
// code path through RoundDuration that rounds to the nearest year:

View File

@ -1,17 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.round
description: >
BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the
options value
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarDateAddUndefinedOptions();
const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9);
const instance = new Temporal.ZonedDateTime(7200_000_000_000n, timeZone, calendar);
instance.round({ smallestUnit: "day" });
assert.sameValue(calendar.dateAddCallCount, 1);

View File

@ -1,31 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.round
description: dateAdd() is called with the correct three arguments
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
let actual = [];
class Calendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
dateAdd(...args) {
actual.push(this, ...args);
return super.dateAdd(...args);
}
}
const calendar = new Calendar();
const zdt = new Temporal.ZonedDateTime(0n, "UTC", calendar);
const result = zdt.round({ smallestUnit: "day" });
assert.sameValue(result.epochNanoseconds, 0n, "Result");
assert.sameValue(actual.length, 4, "three arguments");
assert.sameValue(actual[0], calendar, "this value");
TemporalHelpers.assertPlainDate(actual[1], 1970, 1, "M01", 1, "date argument");
TemporalHelpers.assertDuration(actual[2], 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "duration argument");
assert.sameValue(actual[3], undefined, "options should be undefined");

View File

@ -7,18 +7,18 @@ description: RangeError thrown if the calculated day length is zero
features: [Temporal]
---*/
class Calendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
dateAdd(d) {
return d;
class TimeZone extends Temporal.TimeZone {
#calls = 0;
getPossibleInstantsFor(dateTime) {
if (++this.#calls === 2) {
return super.getPossibleInstantsFor(dateTime.withCalendar("iso8601").subtract({ days: 1 }));
}
return super.getPossibleInstantsFor(dateTime);
}
}
const zdt = new Temporal.ZonedDateTime(0n, "UTC", new Calendar());
const units = ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
for (const smallestUnit of units) {
assert.throws(RangeError, () => zdt.round({ smallestUnit, roundingIncrement: 2 }));
const zdt = new Temporal.ZonedDateTime(0n, new TimeZone("UTC"));
assert.throws(RangeError, () => zdt.round({ smallestUnit, roundingIncrement: 2 }), `zero day-length with smallestUnit ${smallestUnit}`);
}

View File

@ -24,9 +24,7 @@ const expected = [
// GetInstantFor on preceding midnight
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
// AddZonedDateTime
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
// AddDaysToZonedDateTime
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
// InterpretISODateTimeOffset

View File

@ -15,54 +15,39 @@ const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600
const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar);
// Basic difference with largestUnit larger than days.
// The calls come from these paths:
// ZonedDateTime.since() -> DifferenceZonedDateTime ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// The call comes from this path:
// ZonedDateTime.since() -> DifferenceZonedDateTime -> AddZonedDateTime ->
// BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar);
later1.since(earlier, { largestUnit: "weeks" });
assert.sameValue(calendar.dateAddCallCount, 2, "basic difference with largestUnit >days");
// Basic difference with largestUnit equal to days, to cover the second path in
// AddZonedDateTime.
// The calls come from these paths:
// ZonedDateTime.since() -> DifferenceZonedDateTime -> NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
calendar.dateAddCallCount = 0;
later1.since(earlier, { largestUnit: "days" });
assert.sameValue(calendar.dateAddCallCount, 2, "basic difference with largestUnit days");
assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days");
// Difference with rounding, with smallestUnit a calendar unit.
// The calls come from these paths:
// ZonedDateTime.since() ->
// DifferenceZonedDateTime ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// RoundDuration ->
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// MoveRelativeDate -> calendar.dateAdd()
calendar.dateAddCallCount = 0;
later1.since(earlier, { smallestUnit: "weeks" });
assert.sameValue(calendar.dateAddCallCount, 5, "rounding difference with calendar smallestUnit");
assert.sameValue(calendar.dateAddCallCount, 3, "rounding difference with calendar smallestUnit");
// Difference with rounding, with smallestUnit a non-calendar unit, and having
// the resulting time difference be longer than a calendar day, covering the
// paths that go through AdjustRoundedDurationDays. (The path through
// AdjustRoundedDurationDays -> AddDuration that's covered in the corresponding
// test in until() only happens in one direction.)
// The calls come from these paths:
// ZonedDateTime.since() ->
// DifferenceZonedDateTime -> NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (3x)
// AdjustRoundedDurationDays ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (3x)
// The calls come from this path:
// ZonedDateTime.since() -> AdjustRoundedDurationDays -> AddZonedDateTime ->
// BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (3x)
calendar.dateAddCallCount = 0;
const later2 = new Temporal.ZonedDateTime(86_399_999_999_999n, timeZone, calendar);
later2.since(earlier, { largestUnit: "days", smallestUnit: "hours", roundingMode: "ceil" });
assert.sameValue(calendar.dateAddCallCount, 6, "rounding difference with non-calendar smallestUnit and time difference longer than a calendar day");
assert.sameValue(calendar.dateAddCallCount, 3, "rounding difference with non-calendar smallestUnit and time difference longer than a calendar day");

View File

@ -170,16 +170,9 @@ const expectedOpsForCalendarDifference = [
// NanosecondsToDays → DifferenceISODateTime
"get this.calendar.dateUntil",
"call this.calendar.dateUntil",
// NanosecondsToDays → AddZonedDateTime
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
// NanosecondsToDays → AddDaysToZonedDateTime
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
// BalanceDuration → AddZonedDateTime
"get this.timeZone.getOffsetNanosecondsFor",
"call this.timeZone.getOffsetNanosecondsFor",
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
];
@ -203,11 +196,7 @@ const expectedOpsForCalendarRounding = [
// RoundDuration → NanosecondsToDays → DifferenceISODateTime
"get this.calendar.dateUntil",
"call this.calendar.dateUntil",
// RoundDuration → NanosecondsToDays → AddZonedDateTime
"get this.timeZone.getOffsetNanosecondsFor",
"call this.timeZone.getOffsetNanosecondsFor",
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
// RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
];

View File

@ -15,55 +15,36 @@ const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600
const earlier = new Temporal.ZonedDateTime(0n, timeZone, calendar);
// Basic difference with largestUnit larger than days.
// The calls come from these paths:
// ZonedDateTime.until() -> DifferenceZonedDateTime ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// The call comes from this path:
// ZonedDateTime.until() -> DifferenceZonedDateTime -> AddZonedDateTime ->
// BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
const later1 = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calendar);
earlier.until(later1, { largestUnit: "weeks" });
assert.sameValue(calendar.dateAddCallCount, 2, "basic difference with largestUnit >days");
// Basic difference with largestUnit equal to days, to cover the second path in
// AddZonedDateTime.
// The calls come from these paths:
// ZonedDateTime.until() -> DifferenceZonedDateTime -> NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
calendar.dateAddCallCount = 0;
earlier.until(later1, { largestUnit: "days" });
assert.sameValue(calendar.dateAddCallCount, 2, "basic difference with largestUnit days");
assert.sameValue(calendar.dateAddCallCount, 1, "basic difference with largestUnit >days");
// Difference with rounding, with smallestUnit a calendar unit.
// The calls come from these paths:
// ZonedDateTime.until() ->
// DifferenceZonedDateTime ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// RoundDuration ->
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// MoveRelativeDate -> calendar.dateAdd()
calendar.dateAddCallCount = 0;
earlier.until(later1, { smallestUnit: "weeks" });
assert.sameValue(calendar.dateAddCallCount, 5, "rounding difference with calendar smallestUnit");
assert.sameValue(calendar.dateAddCallCount, 3, "rounding difference with calendar smallestUnit");
// Difference with rounding, with smallestUnit a non-calendar unit, and having
// the resulting time difference be longer than a calendar day, covering the
// paths that go through AdjustRoundedDurationDays.
// The calls come from these paths:
// ZonedDateTime.until() ->
// DifferenceZonedDateTime -> NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// AdjustRoundedDurationDays ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// AddDuration ->
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
// DifferenceZonedDateTime -> NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x)
// The call comes from this path:
// ZonedDateTime.until() -> AdjustRoundedDurationDays -> AddZonedDateTime ->
// BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
calendar.dateAddCallCount = 0;
const later2 = new Temporal.ZonedDateTime(86_399_999_999_999n, timeZone, calendar);
earlier.until(later2, { largestUnit: "days", smallestUnit: "hours", roundingMode: "ceil" });
assert.sameValue(calendar.dateAddCallCount, 5, "rounding difference with non-calendar smallestUnit and time difference longer than a calendar day");
assert.sameValue(calendar.dateAddCallCount, 1, "rounding difference with non-calendar smallestUnit and time difference longer than a calendar day");

View File

@ -170,16 +170,9 @@ const expectedOpsForCalendarDifference = [
// NanosecondsToDays → DifferenceISODateTime
"get this.calendar.dateUntil",
"call this.calendar.dateUntil",
// NanosecondsToDays → AddZonedDateTime
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
// NanosecondsToDays → AddDaysToZonedDateTime
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
// BalanceDuration → AddZonedDateTime
"get this.timeZone.getOffsetNanosecondsFor",
"call this.timeZone.getOffsetNanosecondsFor",
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
];
@ -203,11 +196,7 @@ const expectedOpsForCalendarRounding = [
// RoundDuration → NanosecondsToDays → DifferenceISODateTime
"get this.calendar.dateUntil",
"call this.calendar.dateUntil",
// RoundDuration → NanosecondsToDays → AddZonedDateTime
"get this.timeZone.getOffsetNanosecondsFor",
"call this.timeZone.getOffsetNanosecondsFor",
"get this.calendar.dateAdd",
"call this.calendar.dateAdd",
// RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
"get this.timeZone.getPossibleInstantsFor",
"call this.timeZone.getPossibleInstantsFor",
];