Temporal operation NanosecondsToDays: Test infinite or arbitrary-length observable loops in Duration and ZonedDateTime

This commit is contained in:
Cam Tenny 2022-12-13 18:46:20 -08:00 committed by Philip Chimento
parent 6fcebf2f85
commit 09f863d8c3
13 changed files with 742 additions and 84 deletions

View File

@ -1382,6 +1382,20 @@ var TemporalHelpers = {
});
},
/*
* observeMethod(calls, object, propertyName, value):
*
* Defines an own property @object.@propertyName with value @value, that
* will log any calls of @value to the array @calls.
*/
observeMethod(calls, object, propertyName, objectName = "") {
const method = object[propertyName];
object[propertyName] = function () {
calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
return method.apply(object, arguments);
};
},
/*
* Used for substituteMethod to indicate default behavior instead of a
* substituted value

View File

@ -0,0 +1,58 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
15. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
...
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the second call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
TemporalHelpers.SUBSTITUTE_SKIP,
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.add(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 2,
"Expected duration.add to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.add(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 2,
"Expected duration.add to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,71 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
18. Repeat, while done is false,
a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
c. If (nanoseconds - dayLengthNs) × sign 0, then
i. Set nanoseconds to nanoseconds - dayLengthNs.
ii. Set intermediateNs to oneDayFartherNs.
iii. Set days to days + sign.
d. Else,
i. Set done to true.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
function createRelativeTo(count) {
const dayLengthNs = 86400000000000n;
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.add(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 1,
"Expected duration.add to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.add(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 1,
"Expected duration.add to call getPossibleInstantsFor correct number of times"
);

View File

@ -1,10 +1,9 @@
// Copyright (C) 2022 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: >
NanosecondsToDays can loop indefinitely.
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
@ -19,54 +18,41 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Intentionally not Test262Error to ensure assertion errors are correctly propagated.
class StopExecution extends Error {}
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
const stopAt = 1000;
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the first call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
// Always add two days to the start date, this ensures the intermediate date
// is larger than the end date.
let twoDays = Temporal.Duration.from({days: 2});
// Number of calls to dateAdd.
let count = 0;
let cal = new class extends Temporal.Calendar {
// Set `days` to a number larger than `Number.MAX_SAFE_INTEGER`.
dateUntil(start, end, options) {
return Temporal.Duration.from({days: Number.MAX_VALUE});
}
dateAdd(date, duration, options) {
// Stop when we've reached the test limit.
count += 1;
if (count === stopAt) {
throw new StopExecution();
}
if (count === 1) {
return Temporal.Calendar.prototype.dateAdd.call(this, date, duration, options);
}
TemporalHelpers.assertPlainDate(date, 1970, 1, "M01", 1);
TemporalHelpers.assertDuration(
duration,
0, 0, 0, Number.MAX_VALUE,
0, 0, 0,
0, 0, 0,
);
return Temporal.Calendar.prototype.dateAdd.call(this, date, twoDays, options);
}
}("iso8601");
let zdt = new Temporal.ZonedDateTime(0n, "UTC", cal);
let duration = Temporal.Duration.from({days: 1});
let options = {
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.round({
largestUnit: "days",
relativeTo: zdt,
};
});
assert.sameValue(
calls.length,
200 + 2,
"Expected duration.round to call getPossibleInstantsFor correct number of times"
);
assert.throws(StopExecution, () => duration.round(options));
assert.sameValue(count, stopAt);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.round({
largestUnit: "days",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 2,
"Expected duration.round to call getPossibleInstantsFor correct number of times"
);

View File

@ -1,10 +1,9 @@
// Copyright (C) 2022 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: >
NanosecondsToDays can loop indefinitely.
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
@ -23,43 +22,51 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
// Intentionally not Test262Error to ensure assertion errors are correctly propagated.
class StopExecution extends Error {}
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
const stopAt = 1000;
// Number of calls to getPossibleInstantsFor.
let count = 0;
// UTC time zones so we don't have to worry about time zone offsets.
let tz = new class extends Temporal.TimeZone {
getPossibleInstantsFor(dt) {
// Stop when we've reached the test limit.
count += 1;
if (count === stopAt) {
throw new StopExecution();
}
if (count < 4) {
// The first couple calls request the instant for 1970-01-02.
TemporalHelpers.assertPlainDateTime(dt, 1970, 1, "M01", 2, 0, 0, 0, 0, 0, 0);
} else {
// Later on the instant for 1970-01-03 is requested.
TemporalHelpers.assertPlainDateTime(dt, 1970, 1, "M01", 3, 0, 0, 0, 0, 0, 0);
}
// Always return "1970-01-02T00:00:00Z". This leads to iterating indefinitely
// in NanosecondsToDays.
return [new Temporal.Instant(86400000000000n)];
function createRelativeTo(count) {
const dayLengthNs = 86400000000000n;
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
}("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = new Temporal.ZonedDateTime(0n, tz);
let duration = Temporal.Duration.from({days: 1});
let options = {
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.round({
smallestUnit: "days",
relativeTo: zdt,
};
});
assert.sameValue(
calls.length,
200 + 1,
"Expected duration.round to call getPossibleInstantsFor correct number of times"
);
assert.throws(StopExecution, () => duration.round(options));
assert.sameValue(count, stopAt);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.round({
smallestUnit: "days",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 1,
"Expected duration.round to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
15. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
...
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
const other = Temporal.Duration.from({ hours: 1 });
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the second call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
TemporalHelpers.SUBSTITUTE_SKIP,
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.subtract(other, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 2,
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.subtract(other, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 2,
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,71 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
18. Repeat, while done is false,
a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
c. If (nanoseconds - dayLengthNs) × sign 0, then
i. Set nanoseconds to nanoseconds - dayLengthNs.
ii. Set intermediateNs to oneDayFartherNs.
iii. Set days to days + sign.
d. Else,
i. Set done to true.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
function createRelativeTo(count) {
const dayLengthNs = 86400000000000n;
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.subtract(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 1,
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.subtract(duration, {
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 1,
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.total
description: >
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
15. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
...
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the first call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.total({
unit: "day",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 3,
"Expected duration.total to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.total({
unit: "day",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 3,
"Expected duration.total to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,73 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.total
description: >
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
18. Repeat, while done is false,
a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
c. If (nanoseconds - dayLengthNs) × sign 0, then
i. Set nanoseconds to nanoseconds - dayLengthNs.
ii. Set intermediateNs to oneDayFartherNs.
iii. Set days to days + sign.
d. Else,
i. Set done to true.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const duration = Temporal.Duration.from({ days: 1 });
function createRelativeTo(count) {
const dayLengthNs = 86400000000000n;
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
duration.total({
unit: "day",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
200 + 2,
"Expected duration.total to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
duration.total({
unit: "day",
relativeTo: zdt,
});
assert.sameValue(
calls.length,
300 + 2,
"Expected duration.total to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: >
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
15. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
...
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const dayLengthNs = 86400000000000n;
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the second call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
TemporalHelpers.SUBSTITUTE_SKIP,
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
zdt.since(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
200 + 1,
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
zdt.since(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
300 + 1,
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,71 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: >
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
18. Repeat, while done is false,
a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
c. If (nanoseconds - dayLengthNs) × sign 0, then
i. Set nanoseconds to nanoseconds - dayLengthNs.
ii. Set intermediateNs to oneDayFartherNs.
iii. Set days to days + sign.
d. Else,
i. Set done to true.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const dayLengthNs = 86400000000000n;
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
function createRelativeTo(count) {
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
zdt.since(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
200 + 1,
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
zdt.since(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
300 + 1,
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: >
NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
15. If sign is 1, then
a. Repeat, while days > 0 and intermediateNs > endNs,
i. Set days to days - 1.
ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
...
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const dayLengthNs = 86400000000000n;
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
function createRelativeTo(count) {
const tz = new Temporal.TimeZone("UTC");
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor");
const cal = new Temporal.Calendar("iso8601");
// Return _count_ days for the second call to dateUntil, behaving normally after
TemporalHelpers.substituteMethod(cal, "dateUntil", [
TemporalHelpers.SUBSTITUTE_SKIP,
Temporal.Duration.from({ days: count }),
]);
return new Temporal.ZonedDateTime(0n, tz, cal);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
zdt.until(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
200 + 1,
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
zdt.until(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
300 + 1,
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
);

View File

@ -0,0 +1,71 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: >
NanosecondsToDays can loop infinitely.
info: |
NanosecondsToDays ( nanoseconds, relativeTo )
...
18. Repeat, while done is false,
a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]],
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
c. If (nanoseconds - dayLengthNs) × sign 0, then
i. Set nanoseconds to nanoseconds - dayLengthNs.
ii. Set intermediateNs to oneDayFartherNs.
iii. Set days to days + sign.
d. Else,
i. Set done to true.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calls = [];
const dayLengthNs = 86400000000000n;
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
function createRelativeTo(count) {
const dayInstant = new Temporal.Instant(dayLengthNs);
const substitutions = [];
const timeZone = new Temporal.TimeZone("UTC");
// Return constant value for first _count_ calls
TemporalHelpers.substituteMethod(
timeZone,
"getPossibleInstantsFor",
substitutions
);
substitutions.length = count;
let i = 0;
for (i = 0; i < substitutions.length; i++) {
// (this value)
substitutions[i] = [dayInstant];
}
// Record calls in calls[]
TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor");
return new Temporal.ZonedDateTime(0n, timeZone);
}
let zdt = createRelativeTo(200);
calls.splice(0); // Reset calls list after ZonedDateTime construction
zdt.until(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
200 + 1,
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
);
zdt = createRelativeTo(300);
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
zdt.until(other, {
largestUnit: "day",
});
assert.sameValue(
calls.length,
300 + 1,
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
);