mirror of
https://github.com/tc39/test262.git
synced 2025-07-21 04:54:44 +02:00
Temporal: Prevent arbitrary loops in NormalizedTimeDurationToDays
Adapts the tests that checked arbitrarily long loops, to now check that an exception is thrown if the loop would happen. Adds tests that exercise the newly added checks on return values of getPossibleInstantsFor and getOffsetNanosecondsFor that limit UTC offset shifts to 24 hours or less. Also updates some step numbers in related tests.
This commit is contained in:
parent
584048ed08
commit
3e7938c1f5
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.compare
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
const duration1 = new Temporal.Duration(1);
|
||||||
|
const duration2 = new Temporal.Duration(2);
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}), "RangeError should be thrown");
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.compare
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
const duration1 = new Temporal.Duration(1);
|
||||||
|
const duration2 = new Temporal.Duration(2);
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, {relativeTo: relativeTo}), "RangeError should be thrown");
|
@ -4,72 +4,43 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.add
|
esid: sec-temporal.duration.prototype.add
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const duration = Temporal.Duration.from({ days: 1 });
|
const duration = Temporal.Duration.from({ days: 1 });
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
const substitutions = [];
|
let calls = 0;
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
// Return constant value for first _count_ calls
|
getPossibleInstantsFor() {
|
||||||
TemporalHelpers.substituteMethod(
|
calls++;
|
||||||
timeZone,
|
return [dayInstant];
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
let zdt = createRelativeTo(50);
|
const relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
duration.add(duration, {
|
|
||||||
relativeTo: zdt,
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 1,
|
|
||||||
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
assert.throws(RangeError, () => duration.add(duration, { relativeTo }), "arbitrarily long loop is prevented");
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
assert.sameValue(calls, 5, "getPossibleInstantsFor is not called in an arbitrarily long loop");
|
||||||
duration.add(duration, {
|
// Expected calls:
|
||||||
relativeTo: zdt,
|
// AddDuration ->
|
||||||
});
|
// AddZonedDateTime (1)
|
||||||
assert.sameValue(
|
// AddZonedDateTime (2)
|
||||||
calls.length,
|
// DifferenceZonedDateTime ->
|
||||||
100 + 1,
|
// NormalizedTimeDurationToDays ->
|
||||||
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
// AddDaysToZonedDateTime (3, step 12)
|
||||||
);
|
// AddDaysToZonedDateTime (4, step 15)
|
||||||
|
// AddDaysToZonedDateTime (5, step 18.d)
|
||||||
zdt = createRelativeTo(107);
|
|
||||||
assert.throws(RangeError, () => duration.add(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 1);
|
||||||
|
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 1);
|
||||||
|
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "RangeError should be thrown");
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -39,7 +39,7 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
-1n, // Set DifferenceZonedDateTime _ns1_
|
-1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -66,7 +66,7 @@ assert.throws(RangeError, () =>
|
|||||||
"days < 0 and sign = 1"
|
"days < 0 and sign = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
1n, // Set DifferenceZonedDateTime _ns1_
|
1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -93,7 +93,7 @@ assert.throws(RangeError, () =>
|
|||||||
"days > 0 and sign = -1"
|
"days > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Set DifferenceZonedDateTime _ns1_
|
0n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -101,7 +101,7 @@ zdt = new Temporal.ZonedDateTime(
|
|||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
[new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
[new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
[new Temporal.Instant(-4n)], // Returned in step 19, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
@ -121,7 +121,7 @@ assert.throws(RangeError, () =>
|
|||||||
"nanoseconds > 0 and sign = -1"
|
"nanoseconds > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -129,7 +129,7 @@ zdt = new Temporal.ZonedDateTime(
|
|||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[new Temporal.Instant(2n ** 53n + 2n * BigInt(dayNs))],
|
[new Temporal.Instant(2n ** 53n + 2n * BigInt(dayNs))],
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
|
@ -3,74 +3,42 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.round
|
esid: sec-temporal.duration.prototype.round
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const duration = Temporal.Duration.from({ days: 1 });
|
const duration = Temporal.Duration.from({ days: 1 });
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
const substitutions = [];
|
let calls = 0;
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
// Return constant value for first _count_ calls
|
getPossibleInstantsFor() {
|
||||||
TemporalHelpers.substituteMethod(
|
calls++;
|
||||||
timeZone,
|
return [dayInstant];
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
let zdt = createRelativeTo(50);
|
const relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
duration.round({
|
|
||||||
smallestUnit: "days",
|
|
||||||
relativeTo: zdt,
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 1,
|
|
||||||
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
assert.throws(RangeError, () => duration.round({ smallestUnit: "days", relativeTo }), "indefinite loop is prevented");
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
assert.sameValue(calls, 5, "getPossibleInstantsFor is not called indefinitely");
|
||||||
duration.round({
|
// Expected calls:
|
||||||
smallestUnit: "days",
|
// RoundDuration -> MoveRelativeZonedDateTime -> AddZonedDateTime (1)
|
||||||
relativeTo: zdt,
|
// BalanceTimeDurationRelative ->
|
||||||
});
|
// AddZonedDateTime (2)
|
||||||
assert.sameValue(
|
// NormalizedTimeDurationToDays ->
|
||||||
calls.length,
|
// AddDaysToZonedDateTime (3, step 12)
|
||||||
100 + 1,
|
// AddDaysToZonedDateTime (4, step 15)
|
||||||
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
// AddDaysToZonedDateTime (5, step 18.d)
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(107);
|
|
||||||
assert.throws(RangeError, () => duration.round({ smallestUnit: "days", relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.round
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
|
||||||
|
assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.round
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
|
||||||
|
assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "RangeError should be thrown");
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -40,7 +40,7 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -61,7 +61,7 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days < 0 and sign = 1"
|
"RangeError when days < 0 and sign = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -82,13 +82,13 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days > 0 and sign = -1"
|
"RangeError when days > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
[
|
[
|
||||||
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
[new Temporal.Instant(-4n)], // Returned in step 19, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
||||||
@ -106,12 +106,12 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when nanoseconds > 0 and sign = -1"
|
"RangeError when nanoseconds > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
// Not called in step 16 because _days_ = 0
|
// Not called in step 16 because _days_ = 0
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[[new Temporal.Instant(2n ** 53n)]],
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -4,72 +4,43 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.subtract
|
esid: sec-temporal.duration.prototype.subtract
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const duration = Temporal.Duration.from({ days: 1 });
|
const duration = Temporal.Duration.from({ days: 1 });
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
const substitutions = [];
|
let calls = 0;
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
// Return constant value for first _count_ calls
|
getPossibleInstantsFor() {
|
||||||
TemporalHelpers.substituteMethod(
|
calls++;
|
||||||
timeZone,
|
return [dayInstant];
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
let zdt = createRelativeTo(50);
|
const relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
duration.subtract(duration, {
|
|
||||||
relativeTo: zdt,
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 1,
|
|
||||||
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
assert.throws(RangeError, () => duration.subtract(duration, { relativeTo }), "indefinite loop is prevented");
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
assert.sameValue(calls, 5, "getPossibleInstantsFor is not called indefinitely");
|
||||||
duration.subtract(duration, {
|
// Expected calls:
|
||||||
relativeTo: zdt,
|
// AddDuration ->
|
||||||
});
|
// AddZonedDateTime (1)
|
||||||
assert.sameValue(
|
// AddZonedDateTime (2)
|
||||||
calls.length,
|
// DifferenceZonedDateTime ->
|
||||||
100 + 1,
|
// NormalizedTimeDurationToDays ->
|
||||||
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
// AddDaysToZonedDateTime (3, step 12)
|
||||||
);
|
// AddDaysToZonedDateTime (4, step 15)
|
||||||
|
// AddDaysToZonedDateTime (5, step 18.d)
|
||||||
zdt = createRelativeTo(107);
|
|
||||||
assert.throws(RangeError, () => duration.subtract(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 1);
|
||||||
|
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 1);
|
||||||
|
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "RangeError should be thrown");
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -39,7 +39,7 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
-1n, // Set DifferenceZonedDateTime _ns1_
|
-1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -65,7 +65,7 @@ assert.throws(RangeError, () =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
1n, // Set DifferenceZonedDateTime _ns1_
|
1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -91,7 +91,7 @@ assert.throws(RangeError, () =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Set DifferenceZonedDateTime _ns1_
|
0n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -99,7 +99,7 @@ zdt = new Temporal.ZonedDateTime(
|
|||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
[new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
[new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
[new Temporal.Instant(-4n)], // Returned in step 19, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
@ -118,7 +118,7 @@ assert.throws(RangeError, () =>
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -126,7 +126,7 @@ zdt = new Temporal.ZonedDateTime(
|
|||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[new Temporal.Instant(2n ** 53n - 3n * BigInt(dayNs))],
|
[new Temporal.Instant(2n ** 53n - 3n * BigInt(dayNs))],
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
|
@ -4,74 +4,40 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.total
|
esid: sec-temporal.duration.prototype.total
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const duration = Temporal.Duration.from({ days: 1 });
|
const duration = Temporal.Duration.from({ days: 1 });
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
const substitutions = [];
|
let calls = 0;
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
// Return constant value for first _count_ calls
|
getPossibleInstantsFor() {
|
||||||
TemporalHelpers.substituteMethod(
|
calls++;
|
||||||
timeZone,
|
return [dayInstant];
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
let zdt = createRelativeTo(50);
|
const relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
duration.total({
|
|
||||||
unit: "day",
|
|
||||||
relativeTo: zdt,
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 2,
|
|
||||||
"Expected duration.total to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
assert.throws(RangeError, () => duration.total({ unit: "days", relativeTo }), "indefinite loop is prevented");
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
assert.sameValue(calls, 4, "getPossibleInstantsFor is not called indefinitely");
|
||||||
duration.total({
|
// Expected calls:
|
||||||
unit: "day",
|
// AddZonedDateTime (1)
|
||||||
relativeTo: zdt,
|
// NormalizedTimeDurationToDays ->
|
||||||
});
|
// AddDaysToZonedDateTime (2, step 12)
|
||||||
assert.sameValue(
|
// AddDaysToZonedDateTime (3, step 15)
|
||||||
calls.length,
|
// AddDaysToZonedDateTime (4, step 18.d)
|
||||||
100 + 2,
|
|
||||||
"Expected duration.total to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(106);
|
|
||||||
assert.throws(RangeError, () => duration.total({ unit: "day", relativeTo: zdt }), "106-1 days > 2⁵³ ns");
|
|
||||||
|
@ -70,7 +70,7 @@ function f64Repr(f) {
|
|||||||
|
|
||||||
const tz = new (class extends Temporal.TimeZone {
|
const tz = new (class extends Temporal.TimeZone {
|
||||||
getPossibleInstantsFor() {
|
getPossibleInstantsFor() {
|
||||||
// Called in NormalizedTimeDurationToDays 21.a from RoundDuration 7.b.
|
// Called in NormalizedTimeDurationToDays 19 from RoundDuration 7.b.
|
||||||
// Sets _result_.[[DayLength]] to 2⁵³ - 1 ns, its largest possible value
|
// Sets _result_.[[DayLength]] to 2⁵³ - 1 ns, its largest possible value
|
||||||
return [new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)];
|
return [new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
|
||||||
|
assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
|
||||||
|
assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "RangeError should be thrown");
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -40,7 +40,7 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -61,7 +61,7 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days < 0 and sign = 1"
|
"RangeError when days < 0 and sign = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -82,13 +82,13 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days > 0 and sign = -1"
|
"RangeError when days > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets _startNs_ to 0
|
0n, // Sets _startNs_ to 0
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
[
|
[
|
||||||
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
[new Temporal.Instant(-4n)], // Returned in step 19, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
||||||
@ -106,12 +106,12 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when nanoseconds > 0 and sign = -1"
|
"RangeError when nanoseconds > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
// Not called in step 16 because _days_ = 0
|
// Not called in step 16 because _days_ = 0
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[[new Temporal.Instant(2n ** 53n)]],
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindate.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDate(1970, 1, 1);
|
||||||
|
instance.toZonedDateTime({ timeZone, plainTime: new Temporal.PlainTime(12) });
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindate.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDate(1970, 1, 1);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, plainTime: new Temporal.PlainTime(12) }), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindate.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDate(1970, 1, 1);
|
||||||
|
instance.toZonedDateTime({ timeZone, plainTime: new Temporal.PlainTime(12) });
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindate.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDate(1970, 1, 1);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, plainTime: new Temporal.PlainTime(12) }), "RangeError should be thrown");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDateTime(1970, 1, 1, 12);
|
||||||
|
instance.toZonedDateTime(timeZone);
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDateTime(1970, 1, 1, 12);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDateTime(1970, 1, 1, 12);
|
||||||
|
instance.toZonedDateTime(timeZone);
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaindatetime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainDateTime(1970, 1, 1, 12);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime(timeZone), "RangeError should be thrown");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaintime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainTime(12);
|
||||||
|
instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(1970, 1, 1) });
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaintime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainTime(12);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(1970, 1, 1) }), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaintime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainTime(12);
|
||||||
|
instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(1970, 1, 1) });
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.plaintime.prototype.tozoneddatetime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.PlainTime(12);
|
||||||
|
assert.throws(RangeError, () => instance.toZonedDateTime({ timeZone, plainDate: new Temporal.PlainDate(1970, 1, 1) }), "RangeError should be thrown");
|
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.timezone.prototype.getinstantfor
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
timeZone.getInstantFor(new Temporal.PlainDateTime(1970, 1, 1, 12), { disambiguation });
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
||||||
|
calls = 0;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.timezone.prototype.getinstantfor
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
assert.throws(RangeError, () => timeZone.getInstantFor(new Temporal.PlainDateTime(1970, 1, 1, 12), { disambiguation }), "RangeError should be thrown");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.timezone.prototype.getinstantfor
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
timeZone.getInstantFor(new Temporal.PlainDateTime(1970, 1, 1, 12), { disambiguation });
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
||||||
|
calls = 0;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.timezone.prototype.getinstantfor
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
assert.throws(RangeError, () => timeZone.getInstantFor(new Temporal.PlainDateTime(1970, 1, 1, 12), { disambiguation }), "RangeError should be thrown");
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.compare
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
const datetime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime), "RangeError should be thrown (first argument)");
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg), "RangeError should be thrown (second argument)");
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.compare
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
const datetime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(arg, datetime), "RangeError should be thrown (first argument)");
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.compare(datetime, arg), "RangeError should be thrown (second argument)");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.from
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg), "RangeError should be thrown");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.from
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
assert.throws(RangeError, () => Temporal.ZonedDateTime.from(arg), "RangeError should be thrown");
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.equals
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.equals(arg), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.equals
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.equals(arg), "RangeError should be thrown");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.hoursinday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.hoursInDay;
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.hoursinday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.hoursInDay, "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.hoursinday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.hoursInDay;
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.hoursinday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.hoursInDay, "RangeError should be thrown");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.round({ smallestUnit: "hours" });
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.round({ smallestUnit: "hours" }), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.round({ smallestUnit: "hours" });
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.round({ smallestUnit: "hours" }), "RangeError should be thrown");
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.since(arg), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.since(arg), "RangeError should be thrown");
|
@ -4,72 +4,38 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.zoneddatetime.prototype.since
|
esid: sec-temporal.zoneddatetime.prototype.since
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
|
let calls = 0;
|
||||||
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
|
getPossibleInstantsFor() {
|
||||||
|
calls++;
|
||||||
|
return [dayInstant];
|
||||||
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
|
const zdt = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
|
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
assert.throws(RangeError, () => zdt.since(other, { largestUnit: "day" }), "indefinite loop is prevented");
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
assert.sameValue(calls, 3, "getPossibleInstantsFor is not called indefinitely");
|
||||||
const substitutions = [];
|
// Expected calls:
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
// DifferenceZonedDateTime -> NormalizedTimeDurationToDays ->
|
||||||
// Return constant value for first _count_ calls
|
// AddDaysToZonedDateTime (3, step 12)
|
||||||
TemporalHelpers.substituteMethod(
|
// AddDaysToZonedDateTime (4, step 15)
|
||||||
timeZone,
|
// AddDaysToZonedDateTime (5, step 18.d)
|
||||||
"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(50);
|
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
zdt.since(other, {
|
|
||||||
largestUnit: "day",
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 1,
|
|
||||||
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
|
||||||
zdt.since(other, {
|
|
||||||
largestUnit: "day",
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
100 + 1,
|
|
||||||
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(105);
|
|
||||||
assert.throws(RangeError, () => zdt.since(other, { largestUnit: "day" }), "105 days > 2⁵³ ns");
|
|
||||||
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -41,7 +41,7 @@ const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
|||||||
const epochInstant = new Temporal.Instant(0n);
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
const options = { largestUnit: "days" };
|
const options = { largestUnit: "days" };
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let start = new Temporal.ZonedDateTime(
|
let start = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -62,7 +62,7 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -83,7 +83,7 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -104,12 +104,12 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
// Not called in step 16 because _days_ = 0
|
// Not called in step 16 because _days_ = 0
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[[new Temporal.Instant(2n ** 53n)]],
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.startofday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.startOfDay();
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.startofday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.startOfDay(), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.startofday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.startOfDay();
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.startofday
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.startOfDay(), "RangeError should be thrown");
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.until(arg), "RangeError should be thrown");
|
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (C) 2024 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: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
const arg = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.until(arg), "RangeError should be thrown");
|
@ -4,72 +4,38 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.zoneddatetime.prototype.until
|
esid: sec-temporal.zoneddatetime.prototype.until
|
||||||
description: >
|
description: >
|
||||||
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
...
|
...
|
||||||
21. Repeat, while done is false,
|
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||||
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
a. Set _norm_ to _oneDayLess_.
|
||||||
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
b. Set _relativeResult_ to _oneDayFarther_.
|
||||||
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
c. Set _days_ to _days_ + _sign_.
|
||||||
relativeResult.[[EpochNanoseconds]]).
|
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||||
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||||
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||||
i. Set norm to oneDayLess.
|
i. Throw a *RangeError* exception.
|
||||||
ii. Set relativeResult to oneDayFarther.
|
|
||||||
iii. Set days to days + sign.
|
|
||||||
d. Else,
|
|
||||||
i. Set done to true.
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
features: [Temporal]
|
features: [Temporal]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
const calls = [];
|
|
||||||
const dayLengthNs = 86400000000000n;
|
const dayLengthNs = 86400000000000n;
|
||||||
|
const dayInstant = new Temporal.Instant(dayLengthNs);
|
||||||
|
let calls = 0;
|
||||||
|
const timeZone = new class extends Temporal.TimeZone {
|
||||||
|
getPossibleInstantsFor() {
|
||||||
|
calls++;
|
||||||
|
return [dayInstant];
|
||||||
|
}
|
||||||
|
}("UTC");
|
||||||
|
|
||||||
|
const zdt = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
|
const other = new Temporal.ZonedDateTime(dayLengthNs, "UTC", "iso8601");
|
||||||
|
|
||||||
function createRelativeTo(count) {
|
assert.throws(RangeError, () => zdt.until(other, { largestUnit: "day" }), "indefinite loop is prevented");
|
||||||
const dayInstant = new Temporal.Instant(dayLengthNs);
|
assert.sameValue(calls, 3, "getPossibleInstantsFor is not called indefinitely");
|
||||||
const substitutions = [];
|
// Expected calls:
|
||||||
const timeZone = new Temporal.TimeZone("UTC");
|
// DifferenceZonedDateTime -> NormalizedTimeDurationToDays ->
|
||||||
// Return constant value for first _count_ calls
|
// AddDaysToZonedDateTime (3, step 12)
|
||||||
TemporalHelpers.substituteMethod(
|
// AddDaysToZonedDateTime (4, step 15)
|
||||||
timeZone,
|
// AddDaysToZonedDateTime (5, step 18.d)
|
||||||
"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(50);
|
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
|
||||||
zdt.until(other, {
|
|
||||||
largestUnit: "day",
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
50 + 1,
|
|
||||||
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(100);
|
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
|
||||||
zdt.until(other, {
|
|
||||||
largestUnit: "day",
|
|
||||||
});
|
|
||||||
assert.sameValue(
|
|
||||||
calls.length,
|
|
||||||
100 + 1,
|
|
||||||
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
|
|
||||||
);
|
|
||||||
|
|
||||||
zdt = createRelativeTo(105);
|
|
||||||
assert.throws(RangeError, () => zdt.until(other, { largestUnit: "day" }), "105 days > 2⁵³ ns");
|
|
||||||
|
@ -7,12 +7,12 @@ description: >
|
|||||||
RangeErrors.
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
22. If days < 0 and sign = 1, throw a RangeError exception.
|
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
23. If days > 0 and sign = -1, throw a RangeError exception.
|
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
29. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -41,7 +41,7 @@ const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
|||||||
const epochInstant = new Temporal.Instant(0n);
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
const options = { largestUnit: "days" };
|
const options = { largestUnit: "days" };
|
||||||
|
|
||||||
// Step 22: days < 0 and sign = 1
|
// Step 23: days < 0 and sign = 1
|
||||||
let start = new Temporal.ZonedDateTime(
|
let start = new Temporal.ZonedDateTime(
|
||||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -62,7 +62,7 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 23: days > 0 and sign = -1
|
// Step 24: days > 0 and sign = -1
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -83,7 +83,7 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 25: nanoseconds > 0 and sign = -1
|
// Step 26: nanoseconds > 0 and sign = -1
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
@ -104,12 +104,12 @@ assert.throws(RangeError, () =>
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 28: day length is an unsafe integer
|
// Step 29: day length is an unsafe integer
|
||||||
start = new Temporal.ZonedDateTime(
|
start = new Temporal.ZonedDateTime(
|
||||||
0n,
|
0n,
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
// Not called in step 16 because _days_ = 0
|
// Not called in step 16 because _days_ = 0
|
||||||
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
// Returned in step 19, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
[[new Temporal.Instant(2n ** 53n)]],
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.with
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
instance.with({ day: 1 }, { disambiguation });
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
||||||
|
calls = 0;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.with
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
assert.throws(RangeError, () => instance.with({ day: 1 }, { disambiguation }), "RangeError should be thrown");
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.with
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
instance.with({ day: 1 }, { disambiguation });
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
||||||
|
calls = 0;
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.with
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
for (const disambiguation of ["earlier", "later", "compatible"]) {
|
||||||
|
assert.throws(RangeError, () => instance.with({ day: 1 }, { disambiguation }), "RangeError should be thrown");
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaindate
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.withPlainDate(new Temporal.PlainDate(1970, 1, 1));
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaindate
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.withPlainDate(new Temporal.PlainDate(1970, 1, 1)), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaindate
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.withPlainDate(new Temporal.PlainDate(1970, 1, 1));
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaindate
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.withPlainDate(new Temporal.PlainDate(1970, 1, 1)), "RangeError should be thrown");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaintime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
calls++;
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.withPlainTime();
|
||||||
|
|
||||||
|
assert(calls >= 2, "getOffsetNanosecondsFor should be called at least twice");
|
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaintime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
|
||||||
|
in DisambiguatePossibleInstants cannot be greater than 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
DisambiguatePossibleInstants:
|
||||||
|
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
_shiftEpochNs = 0n
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
|
||||||
|
return 12 * 3600e9 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
|
||||||
|
const { year, month, day } = plainDateTime;
|
||||||
|
|
||||||
|
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
|
||||||
|
if (year === 1970 && month === 1 && day === 1) return [];
|
||||||
|
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.withPlainTime(), "RangeError should be thrown");
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaintime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
let calls = 0;
|
||||||
|
|
||||||
|
class Shift24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
calls++;
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12 }),
|
||||||
|
utcInstant.add({ hours: 12 })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new Shift24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
instance.withPlainTime();
|
||||||
|
|
||||||
|
assert(calls >= 1, "getPossibleInstantsFor should be called at least once");
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.withplaintime
|
||||||
|
description: >
|
||||||
|
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
|
||||||
|
features: [Temporal]
|
||||||
|
info: |
|
||||||
|
GetPossibleInstantsFor:
|
||||||
|
5.b.i. Let _numResults_ be _list_'s length.
|
||||||
|
ii. If _numResults_ > 1, then
|
||||||
|
1. Let _epochNs_ be a new empty List.
|
||||||
|
2. For each value _instant_ in list, do
|
||||||
|
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
|
||||||
|
3. Let _min_ be the least element of the List _epochNs_.
|
||||||
|
4. Let _max_ be the greatest element of the List _epochNs_.
|
||||||
|
5. If abs(ℝ(_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
|
||||||
|
---*/
|
||||||
|
|
||||||
|
class ShiftLonger24Hour extends Temporal.TimeZone {
|
||||||
|
id = 'TestTimeZone';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('UTC');
|
||||||
|
}
|
||||||
|
|
||||||
|
getOffsetNanosecondsFor(instant) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleInstantsFor(plainDateTime) {
|
||||||
|
const utc = new Temporal.TimeZone("UTC");
|
||||||
|
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
|
||||||
|
return [
|
||||||
|
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
|
||||||
|
utcInstant.add({ hours: 12 }),
|
||||||
|
utcInstant, // add a third value in case the implementation doesn't sort
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = new ShiftLonger24Hour();
|
||||||
|
|
||||||
|
const instance = new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
|
assert.throws(RangeError, () => instance.withPlainTime(), "RangeError should be thrown");
|
Loading…
x
Reference in New Issue
Block a user