mirror of
https://github.com/tc39/test262.git
synced 2025-07-26 15:34:29 +02:00
Temporal: Limit day length calculations to safe integers
NormalizedTimeDurationToDays can no longer loop indefinitely, because at a certain point we will hit the upper bound of MAX_SAFE_INTEGER, so rename the test to reflect that it can loop an arbitrary but limited number of times. Add a test for the RangeError condition in NormalizedTimeDurationToDays when the time zone calculates a day length that is not a safe integer number of nanoseconds. While editing these tests, rename them to match the current name of the AO and make sure the step numbers are up to date. (Normally I wouldn't care so much about that, but these tests can be pretty confusing so it's good to be able to refer to the spec text.)
This commit is contained in:
parent
092337c8d0
commit
01ec9938bb
@ -4,18 +4,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.add
|
esid: sec-temporal.duration.prototype.add
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -48,24 +49,27 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
duration.add(duration, {
|
duration.add(duration, {
|
||||||
relativeTo: zdt,
|
relativeTo: zdt,
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 1,
|
50 + 1,
|
||||||
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
duration.add(duration, {
|
duration.add(duration, {
|
||||||
relativeTo: zdt,
|
relativeTo: zdt,
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 1,
|
100 + 1,
|
||||||
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
"Expected duration.add to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
zdt = createRelativeTo(107);
|
||||||
|
assert.throws(RangeError, () => duration.add(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
@ -3,13 +3,16 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.add
|
esid: sec-temporal.duration.prototype.add
|
||||||
description: >
|
description: >
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
22. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
23. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -36,22 +39,22 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
// Step 22: 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(
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
[epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
[epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
[epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
[epochInstant], // Returned in step 16, setting _relativeResult_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
// Behave normally in 3 calls made prior to NormalizedTimeDurationToDays
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -63,22 +66,22 @@ assert.throws(RangeError, () =>
|
|||||||
"days < 0 and sign = 1"
|
"days < 0 and sign = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
// Step 23: days > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
1n, // Set DifferenceZonedDateTime _ns1_
|
1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
[epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
[epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
[epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
[epochInstant], // Returned in step 16, setting _relativeResult_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -90,23 +93,23 @@ assert.throws(RangeError, () =>
|
|||||||
"days > 0 and sign = -1"
|
"days > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
// Step 25: nanoseconds > 0 and sign = -1
|
||||||
zdt = new Temporal.ZonedDateTime(
|
zdt = new Temporal.ZonedDateTime(
|
||||||
0n, // Set DifferenceZonedDateTime _ns1_
|
0n, // Set DifferenceZonedDateTime _ns1_
|
||||||
timeZoneSubstituteValues(
|
timeZoneSubstituteValues(
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
[new Temporal.Instant(-1n)], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
[new Temporal.Instant(-1n)], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
[new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_
|
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -117,3 +120,24 @@ assert.throws(RangeError, () =>
|
|||||||
}),
|
}),
|
||||||
"nanoseconds > 0 and sign = -1"
|
"nanoseconds > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Step 28: day length is an unsafe integer
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[new Temporal.Instant(2n ** 53n + 2n * BigInt(dayNs))],
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
dayDuration.add(dayDuration, {
|
||||||
|
relativeTo: zdt,
|
||||||
|
}),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
@ -3,18 +3,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.round
|
esid: sec-temporal.duration.prototype.round
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -47,7 +48,7 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
duration.round({
|
duration.round({
|
||||||
smallestUnit: "days",
|
smallestUnit: "days",
|
||||||
@ -55,11 +56,11 @@ duration.round({
|
|||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 1,
|
50 + 1,
|
||||||
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
duration.round({
|
duration.round({
|
||||||
smallestUnit: "days",
|
smallestUnit: "days",
|
||||||
@ -67,6 +68,9 @@ duration.round({
|
|||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 1,
|
100 + 1,
|
||||||
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
"Expected duration.round to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
zdt = createRelativeTo(107);
|
||||||
|
assert.throws(RangeError, () => duration.round({ smallestUnit: "days", relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
@ -3,13 +3,16 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.round
|
esid: sec-temporal.duration.prototype.round
|
||||||
description: >
|
description: >
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
info: |
|
info: |
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
22. If days < 0 and sign = 1, throw a RangeError exception.
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
23. If days > 0 and sign = -1, throw a RangeError exception.
|
||||||
...
|
...
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
features: [Temporal, BigInt]
|
features: [Temporal, BigInt]
|
||||||
includes: [temporalHelpers.js]
|
includes: [temporalHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
@ -37,15 +40,15 @@ function timeZoneSubstituteValues(
|
|||||||
return tz;
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
// Step 22: 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(
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -58,15 +61,15 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days < 0 and sign = 1"
|
"RangeError when days < 0 and sign = 1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
// Step 23: 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(
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -79,18 +82,18 @@ assert.throws(RangeError, () =>
|
|||||||
"RangeError when days > 0 and sign = -1"
|
"RangeError when days > 0 and sign = -1"
|
||||||
);
|
);
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
// Step 25: 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 for NanosecondsToDays step 14, setting _intermediateNs_
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
[new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_
|
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -102,3 +105,21 @@ 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
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
// Not called in step 16 because _days_ = 0
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
oneNsDuration.round({
|
||||||
|
relativeTo: zdt,
|
||||||
|
smallestUnit: "days",
|
||||||
|
}),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
@ -4,18 +4,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.subtract
|
esid: sec-temporal.duration.prototype.subtract
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -48,24 +49,27 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
duration.subtract(duration, {
|
duration.subtract(duration, {
|
||||||
relativeTo: zdt,
|
relativeTo: zdt,
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 1,
|
50 + 1,
|
||||||
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
duration.subtract(duration, {
|
duration.subtract(duration, {
|
||||||
relativeTo: zdt,
|
relativeTo: zdt,
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 1,
|
100 + 1,
|
||||||
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
"Expected duration.subtract to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
zdt = createRelativeTo(107);
|
||||||
|
assert.throws(RangeError, () => duration.subtract(duration, { relativeTo: zdt }), "107-2 days > 2⁵³ ns");
|
@ -1,116 +0,0 @@
|
|||||||
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
|
||||||
// This code is governed by the BSD license found in the LICENSE file.
|
|
||||||
/*---
|
|
||||||
esid: sec-temporal.duration.prototype.subtract
|
|
||||||
description: >
|
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
|
||||||
info: |
|
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
...
|
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
features: [Temporal, BigInt]
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
---*/
|
|
||||||
|
|
||||||
const dayNs = 86_400_000_000_000;
|
|
||||||
const dayDuration = Temporal.Duration.from({ days: 1 });
|
|
||||||
const epochInstant = new Temporal.Instant(0n);
|
|
||||||
|
|
||||||
function timeZoneSubstituteValues(
|
|
||||||
getPossibleInstantsFor,
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
) {
|
|
||||||
const tz = new Temporal.TimeZone("UTC");
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getPossibleInstantsFor",
|
|
||||||
getPossibleInstantsFor
|
|
||||||
);
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getOffsetNanosecondsFor",
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
);
|
|
||||||
return tz;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
|
||||||
-1n, // Set DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
|
||||||
[epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
|
||||||
[epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
],
|
|
||||||
[
|
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
|
||||||
dayDuration.subtract(dayDuration, {
|
|
||||||
relativeTo: zdt,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
|
||||||
zdt = new Temporal.ZonedDateTime(
|
|
||||||
1n, // Set DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
|
||||||
[epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
|
||||||
[epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
],
|
|
||||||
[
|
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
|
||||||
dayDuration.subtract(dayDuration, {
|
|
||||||
relativeTo: zdt,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
|
||||||
zdt = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Set DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9
|
|
||||||
[new Temporal.Instant(-1n)], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
|
||||||
[new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_
|
|
||||||
],
|
|
||||||
[
|
|
||||||
// Behave normally in 3 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
|
||||||
dayDuration.subtract(dayDuration, {
|
|
||||||
relativeTo: zdt,
|
|
||||||
})
|
|
||||||
);
|
|
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.subtract
|
||||||
|
description: >
|
||||||
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
|
info: |
|
||||||
|
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.
|
||||||
|
...
|
||||||
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
|
features: [Temporal, BigInt]
|
||||||
|
includes: [temporalHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
const dayNs = 86_400_000_000_000;
|
||||||
|
const dayDuration = Temporal.Duration.from({ days: 1 });
|
||||||
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
|
|
||||||
|
function timeZoneSubstituteValues(
|
||||||
|
getPossibleInstantsFor,
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
) {
|
||||||
|
const tz = new Temporal.TimeZone("UTC");
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getPossibleInstantsFor",
|
||||||
|
getPossibleInstantsFor
|
||||||
|
);
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getOffsetNanosecondsFor",
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
);
|
||||||
|
return tz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 22: days < 0 and sign = 1
|
||||||
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
|
-1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
|
[epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
|
[epochInstant], // Returned in step 16, setting _relativeResult_
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Behave normally in 3 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
||||||
|
dayDuration.subtract(dayDuration, {
|
||||||
|
relativeTo: zdt,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 23: days > 0 and sign = -1
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
1n, // Set DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 15
|
||||||
|
[epochInstant], // Returned in AddDuration step 16, setting _endNs_ -> DifferenceZonedDateTime _ns2_
|
||||||
|
[epochInstant], // Returned in step 16, setting _relativeResult_
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
||||||
|
dayDuration.subtract(dayDuration, {
|
||||||
|
relativeTo: zdt,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 25: nanoseconds > 0 and sign = -1
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Set DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
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(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
|
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
||||||
|
],
|
||||||
|
[
|
||||||
|
// Behave normally in 3 calls made prior to NanosecondsToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference
|
||||||
|
dayDuration.subtract(dayDuration, {
|
||||||
|
relativeTo: zdt,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 28: day length is an unsafe integer
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 15
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for AddDuration step 16
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for step 16, setting _relativeResult_
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[new Temporal.Instant(2n ** 53n - 3n * BigInt(dayNs))],
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const twoDaysDuration = new Temporal.Duration(0, 0, 0, 2);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
dayDuration.subtract(twoDaysDuration, {
|
||||||
|
relativeTo: zdt,
|
||||||
|
}),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
@ -4,18 +4,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.duration.prototype.total
|
esid: sec-temporal.duration.prototype.total
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -48,7 +49,7 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
duration.total({
|
duration.total({
|
||||||
unit: "day",
|
unit: "day",
|
||||||
@ -56,11 +57,11 @@ duration.total({
|
|||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 2,
|
50 + 2,
|
||||||
"Expected duration.total to call getPossibleInstantsFor correct number of times"
|
"Expected duration.total to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
duration.total({
|
duration.total({
|
||||||
unit: "day",
|
unit: "day",
|
||||||
@ -68,6 +69,9 @@ duration.total({
|
|||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 2,
|
100 + 2,
|
||||||
"Expected duration.total to call getPossibleInstantsFor correct number of times"
|
"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");
|
@ -1,104 +0,0 @@
|
|||||||
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
|
||||||
// This code is governed by the BSD license found in the LICENSE file.
|
|
||||||
/*---
|
|
||||||
esid: sec-temporal.duration.prototype.total
|
|
||||||
description: >
|
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
|
||||||
info: |
|
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
...
|
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
features: [Temporal, BigInt]
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
---*/
|
|
||||||
|
|
||||||
const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 });
|
|
||||||
const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 });
|
|
||||||
const dayNs = 86_400_000_000_000;
|
|
||||||
const epochInstant = new Temporal.Instant(0n);
|
|
||||||
|
|
||||||
function timeZoneSubstituteValues(
|
|
||||||
getPossibleInstantsFor,
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
) {
|
|
||||||
const tz = new Temporal.TimeZone("UTC");
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getPossibleInstantsFor",
|
|
||||||
getPossibleInstantsFor
|
|
||||||
);
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getOffsetNanosecondsFor",
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
);
|
|
||||||
return tz;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
|
||||||
let zdt = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Sets _startNs_ to 0
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Using 1ns duration _nanoseconds_ to 1 and _sign_ to 1
|
|
||||||
oneNsDuration.total({
|
|
||||||
relativeTo: zdt,
|
|
||||||
unit: "day",
|
|
||||||
}),
|
|
||||||
"RangeError when days < 0 and sign = 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
|
||||||
zdt = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Sets _startNs_ to 0
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
|
|
||||||
negOneNsDuration.total({
|
|
||||||
relativeTo: zdt,
|
|
||||||
unit: "day",
|
|
||||||
}),
|
|
||||||
"RangeError when days > 0 and sign = -1"
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
|
||||||
zdt = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Sets _startNs_ to 0
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[
|
|
||||||
[new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_
|
|
||||||
],
|
|
||||||
[
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
// Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
|
|
||||||
negOneNsDuration.total({
|
|
||||||
relativeTo: zdt,
|
|
||||||
unit: "day",
|
|
||||||
}),
|
|
||||||
"RangeError when nanoseconds > 0 and sign = -1"
|
|
||||||
);
|
|
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.duration.prototype.total
|
||||||
|
description: >
|
||||||
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
|
info: |
|
||||||
|
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.
|
||||||
|
...
|
||||||
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
|
features: [Temporal, BigInt]
|
||||||
|
includes: [temporalHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 });
|
||||||
|
const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 });
|
||||||
|
const dayNs = 86_400_000_000_000;
|
||||||
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
|
|
||||||
|
function timeZoneSubstituteValues(
|
||||||
|
getPossibleInstantsFor,
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
) {
|
||||||
|
const tz = new Temporal.TimeZone("UTC");
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getPossibleInstantsFor",
|
||||||
|
getPossibleInstantsFor
|
||||||
|
);
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getOffsetNanosecondsFor",
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
);
|
||||||
|
return tz;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 22: days < 0 and sign = 1
|
||||||
|
let zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Sets _startNs_ to 0
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.total
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Using 1ns duration _nanoseconds_ to 1 and _sign_ to 1
|
||||||
|
oneNsDuration.total({
|
||||||
|
relativeTo: zdt,
|
||||||
|
unit: "day",
|
||||||
|
}),
|
||||||
|
"RangeError when days < 0 and sign = 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 23: days > 0 and sign = -1
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Sets _startNs_ to 0
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.total
|
||||||
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
|
||||||
|
negOneNsDuration.total({
|
||||||
|
relativeTo: zdt,
|
||||||
|
unit: "day",
|
||||||
|
}),
|
||||||
|
"RangeError when days > 0 and sign = -1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 25: nanoseconds > 0 and sign = -1
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Sets _startNs_ to 0
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[
|
||||||
|
[new Temporal.Instant(-2n)], // Returned in step 16, setting _relativeResult_
|
||||||
|
[new Temporal.Instant(-4n)], // Returned in step 21.a, setting _oneDayFarther_
|
||||||
|
],
|
||||||
|
[
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP, // pre-conversion in Duration.p.total
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
// Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1
|
||||||
|
negOneNsDuration.total({
|
||||||
|
relativeTo: zdt,
|
||||||
|
unit: "day",
|
||||||
|
}),
|
||||||
|
"RangeError when nanoseconds > 0 and sign = -1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 28: day length is an unsafe integer
|
||||||
|
zdt = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
// Not called in step 16 because _days_ = 0
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
oneNsDuration.total({
|
||||||
|
relativeTo: zdt,
|
||||||
|
unit: "days",
|
||||||
|
}),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
@ -1,102 +0,0 @@
|
|||||||
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
|
||||||
// This code is governed by the BSD license found in the LICENSE file.
|
|
||||||
/*---
|
|
||||||
esid: sec-temporal.zoneddatetime.prototype.since
|
|
||||||
description: >
|
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
|
||||||
info: |
|
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
...
|
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
features: [Temporal, BigInt]
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
---*/
|
|
||||||
|
|
||||||
function timeZoneSubstituteValues(
|
|
||||||
getPossibleInstantsFor,
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
) {
|
|
||||||
const tz = new Temporal.TimeZone("UTC");
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getPossibleInstantsFor",
|
|
||||||
getPossibleInstantsFor
|
|
||||||
);
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getOffsetNanosecondsFor",
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
);
|
|
||||||
return tz;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dayNs = 86_400_000_000_000;
|
|
||||||
const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
|
|
||||||
const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
|
||||||
const epochInstant = new Temporal.Instant(0n);
|
|
||||||
const options = { largestUnit: "days" };
|
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
|
||||||
let start = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.since(
|
|
||||||
oneZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
|
||||||
start = new Temporal.ZonedDateTime(
|
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.since(
|
|
||||||
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
|
||||||
start = new Temporal.ZonedDateTime(
|
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[new Temporal.Instant(-1n)]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.since(
|
|
||||||
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
@ -4,18 +4,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.zoneddatetime.prototype.since
|
esid: sec-temporal.zoneddatetime.prototype.since
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -48,24 +49,27 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
zdt.since(other, {
|
zdt.since(other, {
|
||||||
largestUnit: "day",
|
largestUnit: "day",
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 1,
|
50 + 1,
|
||||||
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
|
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
zdt.since(other, {
|
zdt.since(other, {
|
||||||
largestUnit: "day",
|
largestUnit: "day",
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 1,
|
100 + 1,
|
||||||
"Expected ZonedDateTime.since to call getPossibleInstantsFor correct number of times"
|
"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");
|
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.since
|
||||||
|
description: >
|
||||||
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
|
info: |
|
||||||
|
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.
|
||||||
|
...
|
||||||
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
|
features: [Temporal, BigInt]
|
||||||
|
includes: [temporalHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
function timeZoneSubstituteValues(
|
||||||
|
getPossibleInstantsFor,
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
) {
|
||||||
|
const tz = new Temporal.TimeZone("UTC");
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getPossibleInstantsFor",
|
||||||
|
getPossibleInstantsFor
|
||||||
|
);
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getOffsetNanosecondsFor",
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
);
|
||||||
|
return tz;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayNs = 86_400_000_000_000;
|
||||||
|
const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
|
||||||
|
const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
||||||
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
|
const options = { largestUnit: "days" };
|
||||||
|
|
||||||
|
// Step 22: days < 0 and sign = 1
|
||||||
|
let start = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.since(
|
||||||
|
oneZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 23: days > 0 and sign = -1
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.since(
|
||||||
|
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 25: nanoseconds > 0 and sign = -1
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.since(
|
||||||
|
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 28: day length is an unsafe integer
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
// Not called in step 16 because _days_ = 0
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.since(
|
||||||
|
oneZDT,
|
||||||
|
options
|
||||||
|
),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
@ -1,102 +0,0 @@
|
|||||||
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
|
||||||
// This code is governed by the BSD license found in the LICENSE file.
|
|
||||||
/*---
|
|
||||||
esid: sec-temporal.zoneddatetime.prototype.until
|
|
||||||
description: >
|
|
||||||
Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime.
|
|
||||||
info: |
|
|
||||||
6.5.7 NanosecondsToDays ( nanoseconds, relativeTo )
|
|
||||||
19. If days < 0 and sign = 1, throw a RangeError exception.
|
|
||||||
20. If days > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
...
|
|
||||||
22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
|
|
||||||
features: [Temporal, BigInt]
|
|
||||||
includes: [temporalHelpers.js]
|
|
||||||
---*/
|
|
||||||
|
|
||||||
function timeZoneSubstituteValues(
|
|
||||||
getPossibleInstantsFor,
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
) {
|
|
||||||
const tz = new Temporal.TimeZone("UTC");
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getPossibleInstantsFor",
|
|
||||||
getPossibleInstantsFor
|
|
||||||
);
|
|
||||||
TemporalHelpers.substituteMethod(
|
|
||||||
tz,
|
|
||||||
"getOffsetNanosecondsFor",
|
|
||||||
getOffsetNanosecondsFor
|
|
||||||
);
|
|
||||||
return tz;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dayNs = 86_400_000_000_000;
|
|
||||||
const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
|
|
||||||
const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
|
||||||
const epochInstant = new Temporal.Instant(0n);
|
|
||||||
const options = { largestUnit: "days" };
|
|
||||||
|
|
||||||
// NanosecondsToDays.19: days < 0 and sign = 1
|
|
||||||
let start = new Temporal.ZonedDateTime(
|
|
||||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.until(
|
|
||||||
oneZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.20: days > 0 and sign = -1
|
|
||||||
start = new Temporal.ZonedDateTime(
|
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.until(
|
|
||||||
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// NanosecondsToDays.22: nanoseconds > 0 and sign = -1
|
|
||||||
start = new Temporal.ZonedDateTime(
|
|
||||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
|
||||||
timeZoneSubstituteValues(
|
|
||||||
[[new Temporal.Instant(-1n)]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_
|
|
||||||
[
|
|
||||||
// Behave normally in 2 calls made prior to NanosecondsToDays
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
|
||||||
dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_
|
|
||||||
-dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert.throws(RangeError, () =>
|
|
||||||
start.until(
|
|
||||||
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
|
||||||
options
|
|
||||||
)
|
|
||||||
);
|
|
@ -4,18 +4,19 @@
|
|||||||
/*---
|
/*---
|
||||||
esid: sec-temporal.zoneddatetime.prototype.until
|
esid: sec-temporal.zoneddatetime.prototype.until
|
||||||
description: >
|
description: >
|
||||||
NanosecondsToDays can loop infinitely.
|
NormalizedTimeDurationToDays can loop arbitrarily up to max safe integer
|
||||||
info: |
|
info: |
|
||||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||||
|
|
||||||
...
|
...
|
||||||
18. Repeat, while done is false,
|
21. Repeat, while done is false,
|
||||||
a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]],
|
a. Let oneDayFarther be ? AddDaysToZonedDateTime(relativeResult.[[Instant]],
|
||||||
relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
|
relativeResult.[[DateTime]], timeZoneRec, zonedRelativeTo.[[Calendar]], sign).
|
||||||
b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
|
b. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(oneDayFarther.[[EpochNanoseconds]],
|
||||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
relativeResult.[[EpochNanoseconds]]).
|
||||||
i. Set nanoseconds to nanoseconds - dayLengthNs.
|
c. Let oneDayLess be ? SubtractNormalizedTimeDuration(norm, dayLengthNs).
|
||||||
ii. Set intermediateNs to oneDayFartherNs.
|
c. If NormalizedTimeDurationSign(oneDayLess) × sign ≥ 0, then
|
||||||
|
i. Set norm to oneDayLess.
|
||||||
|
ii. Set relativeResult to oneDayFarther.
|
||||||
iii. Set days to days + sign.
|
iii. Set days to days + sign.
|
||||||
d. Else,
|
d. Else,
|
||||||
i. Set done to true.
|
i. Set done to true.
|
||||||
@ -48,24 +49,27 @@ function createRelativeTo(count) {
|
|||||||
return new Temporal.ZonedDateTime(0n, timeZone);
|
return new Temporal.ZonedDateTime(0n, timeZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
let zdt = createRelativeTo(200);
|
let zdt = createRelativeTo(50);
|
||||||
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
calls.splice(0); // Reset calls list after ZonedDateTime construction
|
||||||
zdt.until(other, {
|
zdt.until(other, {
|
||||||
largestUnit: "day",
|
largestUnit: "day",
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
200 + 1,
|
50 + 1,
|
||||||
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
|
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
|
||||||
);
|
);
|
||||||
|
|
||||||
zdt = createRelativeTo(300);
|
zdt = createRelativeTo(100);
|
||||||
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction
|
||||||
zdt.until(other, {
|
zdt.until(other, {
|
||||||
largestUnit: "day",
|
largestUnit: "day",
|
||||||
});
|
});
|
||||||
assert.sameValue(
|
assert.sameValue(
|
||||||
calls.length,
|
calls.length,
|
||||||
300 + 1,
|
100 + 1,
|
||||||
"Expected ZonedDateTime.until to call getPossibleInstantsFor correct number of times"
|
"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");
|
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
/*---
|
||||||
|
esid: sec-temporal.zoneddatetime.prototype.until
|
||||||
|
description: >
|
||||||
|
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||||
|
RangeErrors.
|
||||||
|
info: |
|
||||||
|
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.
|
||||||
|
...
|
||||||
|
25. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||||
|
...
|
||||||
|
28. If dayLength ≥ 2⁵³, throw a RangeError exception.
|
||||||
|
features: [Temporal, BigInt]
|
||||||
|
includes: [temporalHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
function timeZoneSubstituteValues(
|
||||||
|
getPossibleInstantsFor,
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
) {
|
||||||
|
const tz = new Temporal.TimeZone("UTC");
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getPossibleInstantsFor",
|
||||||
|
getPossibleInstantsFor
|
||||||
|
);
|
||||||
|
TemporalHelpers.substituteMethod(
|
||||||
|
tz,
|
||||||
|
"getOffsetNanosecondsFor",
|
||||||
|
getOffsetNanosecondsFor
|
||||||
|
);
|
||||||
|
return tz;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayNs = 86_400_000_000_000;
|
||||||
|
const zeroZDT = new Temporal.ZonedDateTime(0n, "UTC");
|
||||||
|
const oneZDT = new Temporal.ZonedDateTime(1n, "UTC");
|
||||||
|
const epochInstant = new Temporal.Instant(0n);
|
||||||
|
const options = { largestUnit: "days" };
|
||||||
|
|
||||||
|
// Step 22: days < 0 and sign = 1
|
||||||
|
let start = new Temporal.ZonedDateTime(
|
||||||
|
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.until(
|
||||||
|
oneZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 23: days > 0 and sign = -1
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[epochInstant]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
-dayNs + 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
dayNs - 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.until(
|
||||||
|
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 25: nanoseconds > 0 and sign = -1
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
[[new Temporal.Instant(-1n)]], // Returned in step 16, setting _relativeResult_
|
||||||
|
[
|
||||||
|
// Behave normally in 2 calls made prior to NormalizedTimeDurationToDays
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||||
|
dayNs - 1, // Returned in step 8, setting _startDateTime_
|
||||||
|
-dayNs + 1, // Returned in step 9, setting _endDateTime_
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.until(
|
||||||
|
zeroZDT, // Sets DifferenceZonedDateTime _ns2_
|
||||||
|
options
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 28: day length is an unsafe integer
|
||||||
|
start = new Temporal.ZonedDateTime(
|
||||||
|
0n,
|
||||||
|
timeZoneSubstituteValues(
|
||||||
|
// Not called in step 16 because _days_ = 0
|
||||||
|
// Returned in step 21.a, making _oneDayFarther_ 2^53 ns later than _relativeResult_
|
||||||
|
[[new Temporal.Instant(2n ** 53n)]],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert.throws(RangeError, () =>
|
||||||
|
start.until(
|
||||||
|
oneZDT,
|
||||||
|
options
|
||||||
|
),
|
||||||
|
"Should throw RangeError when time zone calculates an outrageous day length"
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user