mirror of
https://github.com/tc39/test262.git
synced 2025-04-08 19:35:28 +02:00
Temporal: Tests for round() and since()/until() using the same code path
This should produce all the same results (except for a change to weeks balancing in round(), which is now more consistent with since()/until()) but leads to different observable user code calls. See https://github.com/tc39/proposal-temporal/issues/2742
This commit is contained in:
parent
6c6c72b063
commit
96e31e7e28
@ -17,41 +17,23 @@ const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
|
||||
// Rounding with smallestUnit a calendar unit.
|
||||
// The calls come from these paths:
|
||||
// Duration.round() ->
|
||||
// RoundDuration ->
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
// MoveRelativeDate -> calendar.dateAdd()
|
||||
// BalanceDateDurationRelative -> calendar.dateAdd()
|
||||
// AddZonedDateTime -> calendar.dateAdd()
|
||||
// DifferenceZonedDateTimeWithRounding -> RoundRelativeDuration -> NudgeToCalendarUnit ->
|
||||
// AddDateTime -> calendar.dateAdd() (2x)
|
||||
|
||||
const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
|
||||
instance1.round({ smallestUnit: "weeks", relativeTo });
|
||||
assert.sameValue(calendar.dateAddCallCount, 4, "rounding with calendar smallestUnit");
|
||||
|
||||
// Rounding with a non-default largestUnit to cover the path in
|
||||
// UnbalanceDurationRelative where larger units are converted into smaller
|
||||
// units; and with a smallestUnit larger than days to cover the path in
|
||||
// RoundDuration where days are converted into larger units.
|
||||
// The calls come from these paths:
|
||||
// Duration.round() ->
|
||||
// UnbalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
|
||||
// RoundDuration -> MoveRelativeDate -> calendar.dateAdd() (2x)
|
||||
// BalanceDateDurationRelative -> calendar.dateAdd()
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
|
||||
calendar.dateAddCallCount = 0;
|
||||
|
||||
const instance2 = new Temporal.Duration(0, 1, 1, 1);
|
||||
instance2.round({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo });
|
||||
assert.sameValue(calendar.dateAddCallCount, 6, "rounding with non-default largestUnit and calendar smallestUnit");
|
||||
const instance = new Temporal.Duration(1, 1, 1, 1, 1);
|
||||
instance.round({ smallestUnit: "weeks", relativeTo });
|
||||
assert.sameValue(calendar.dateAddCallCount, 3, "rounding with calendar smallestUnit");
|
||||
|
||||
// Rounding with smallestUnit days only.
|
||||
// The calls come from these paths:
|
||||
// Duration.round() ->
|
||||
// RoundDuration ->
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
// BalanceDateDurationRelative -> calendar.dateAdd()
|
||||
// AddZonedDateTime -> calendar.dateAdd()
|
||||
// DifferenceZonedDateTimeWithRounding ->
|
||||
// RoundDuration -> MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
// BalanceDateDurationRelative -> calendar.dateAdd()
|
||||
|
||||
calendar.dateAddCallCount = 0;
|
||||
|
||||
const instance3 = new Temporal.Duration(1, 1, 1, 1, 1);
|
||||
instance3.round({ smallestUnit: "days", relativeTo });
|
||||
instance.round({ smallestUnit: "days", relativeTo });
|
||||
assert.sameValue(calendar.dateAddCallCount, 3, "rounding with days smallestUnit");
|
||||
|
@ -3,69 +3,9 @@
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.round
|
||||
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
|
||||
info: |
|
||||
sec-temporal.duration.prototype.round steps 23–27:
|
||||
23. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_).
|
||||
24. Let _roundResult_ be (? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_)).[[DurationRecord]].
|
||||
25. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_).
|
||||
26. Let _balanceResult_ be ? BalanceDuration(_adjustResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_).
|
||||
27. Let _result_ be ? BalanceDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _balanceResult_.[[Days]], _largestUnit_, _relativeTo_).
|
||||
sec-temporal-unbalancedatedurationrelative step 3:
|
||||
3. If _largestUnit_ is *"month"*, then
|
||||
...
|
||||
g. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
h. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
|
||||
i. Let _untilResult_ be ? CalendarDateUntil(_calendarRec_.[[Receiver]], _plainRelativeTo_, _later_, _untilOptions_, _calendarRec_.[[DateUntil]]).
|
||||
sec-temporal-roundduration steps 5.d and 8.n–p:
|
||||
5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
|
||||
...
|
||||
8. If _unit_ is *"year"*, then
|
||||
...
|
||||
n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
|
||||
p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
|
||||
sec-temporal-adjustroundeddurationdays steps 1 and 9:
|
||||
1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
|
||||
a. Return ...
|
||||
...
|
||||
9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
|
||||
sec-temporal-addduration step 7.a–g:
|
||||
a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
|
||||
...
|
||||
f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
g. Else,
|
||||
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
|
||||
sec-temporal-balancedurationrelative steps 1, 9.m–o, and 9.q.vi–viii:
|
||||
1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _years_, _months_, _weeks_, and _days_ are all 0, then
|
||||
a. Return ...
|
||||
...
|
||||
9. If _largestUnit_ is *"year"*, then
|
||||
...
|
||||
m. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
n. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
|
||||
o. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
|
||||
p. ...
|
||||
q. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_),
|
||||
...
|
||||
vi. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
vii. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
|
||||
viii. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_).
|
||||
sec-temporal-balanceduration step 3.a:
|
||||
3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
|
||||
sec-temporal-differencezoneddatetime steps 7 and 11:
|
||||
7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
|
||||
11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
|
||||
sec-temporal-nanosecondstodays step 11:
|
||||
11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
|
||||
sec-temporal-differenceisodatetime steps 9–11:
|
||||
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
|
||||
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
|
||||
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
|
||||
description: >
|
||||
The options object passed to calendar.dateUntil has a largestUnit property
|
||||
with its value in the singular form
|
||||
includes: [compareArray.js, temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
@ -99,9 +39,9 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
duration.round({ largestUnit, roundingIncrement: 2, roundingMode: 'ceil', relativeTo });
|
||||
},
|
||||
{
|
||||
years: ["year", "year"],
|
||||
months: ["month", "month"],
|
||||
weeks: ["week", "week"],
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
@ -123,7 +63,7 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
},
|
||||
{
|
||||
years: ["year"],
|
||||
months: ["month", "month"],
|
||||
months: ["month"],
|
||||
weeks: ["week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
|
@ -20,14 +20,8 @@ features: [Temporal]
|
||||
// Based on a test case by André Bargull
|
||||
|
||||
const calendar = new class extends Temporal.Calendar {
|
||||
#dateUntil = 0;
|
||||
|
||||
dateUntil(one, two, options) {
|
||||
let result = super.dateUntil(one, two, options);
|
||||
if (++this.#dateUntil === 2) {
|
||||
result = result.negated();
|
||||
}
|
||||
return result;
|
||||
return super.dateUntil(one, two, options).negated();
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
|
@ -28,8 +28,7 @@ const calendar = new CalendarDateUntilObservable("iso8601");
|
||||
const relativeTo = new Temporal.PlainDate(2018, 10, 12, calendar);
|
||||
|
||||
const expected = [
|
||||
"call dateUntil", // UnbalanceDateDurationRelative
|
||||
"call dateUntil", // BalanceDateDurationRelative
|
||||
"call dateUntil", // DifferencePlainDateTimeWithRounding -> DifferenceISODateTime
|
||||
];
|
||||
|
||||
const years = new Temporal.Duration(2);
|
||||
|
@ -15,7 +15,7 @@ const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
|
||||
|
||||
{
|
||||
// Date part of duration lands on skipped DST hour, causing disambiguation
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 12);
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 11, 30);
|
||||
const relativeTo = new Temporal.ZonedDateTime(
|
||||
950868000_000_000_000n /* = 2000-02-18T10Z */,
|
||||
timeZone); /* = 2000-02-18T02-08 in local time */
|
||||
@ -29,17 +29,35 @@ const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
|
||||
}
|
||||
|
||||
{
|
||||
// Month-only part of duration lands on skipped DST hour, should not cause
|
||||
// disambiguation
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15);
|
||||
// Month-only part of duration lands on skipped DST hour
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 0, 30);
|
||||
const relativeTo = new Temporal.ZonedDateTime(
|
||||
951991200_000_000_000n /* = 2000-03-02T10Z */,
|
||||
timeZone); /* = 2000-03-02T02-08 in local time */
|
||||
|
||||
TemporalHelpers.assertDuration(duration.round({ smallestUnit: "months", relativeTo }),
|
||||
0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
"1 month 15 days should be exactly 1.5 months, which rounds up to 2 months");
|
||||
"1 month 15 days 00:30 should be exactly 1.5 months, which rounds up to 2 months");
|
||||
TemporalHelpers.assertDuration(duration.round({ smallestUnit: "months", roundingMode: 'halfTrunc', relativeTo }),
|
||||
0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
"1 month 15 days should be exactly 1.5 months, which rounds down to 1 month");
|
||||
"1 month 15 days 00:30 should be exactly 1.5 months, which rounds down to 1 month");
|
||||
}
|
||||
|
||||
{
|
||||
// Day rounding
|
||||
// DST spring-forward hour skipped at 2000-04-02T02:00 (23 hour day)
|
||||
// 11.5 hours is 0.5
|
||||
const duration = new Temporal.Duration(0, 0, 0, 0, 11, 30);
|
||||
const instant = timeZone.getPossibleInstantsFor(Temporal.PlainDateTime.from("2000-04-02T00:00:00"))[0];
|
||||
const relativeTo = instant.toZonedDateTimeISO(timeZone);
|
||||
|
||||
TemporalHelpers.assertDuration(
|
||||
duration.round({ relativeTo, smallestUnit: "days" }),
|
||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
|
||||
TemporalHelpers.assertDuration(
|
||||
duration.round({ relativeTo, smallestUnit: "days", roundingMode: "halfTrunc" }),
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
);
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.round
|
||||
description: >
|
||||
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||
...
|
||||
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||
a. Set _norm_ to _oneDayLess_.
|
||||
b. Set _relativeResult_ to _oneDayFarther_.
|
||||
c. Set _days_ to _days_ + _sign_.
|
||||
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||
i. Throw a *RangeError* exception.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
const duration = Temporal.Duration.from({ days: 1 });
|
||||
|
||||
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 relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
|
||||
assert.throws(RangeError, () => duration.round({ smallestUnit: "days", relativeTo }), "indefinite loop is prevented");
|
||||
assert.sameValue(calls, 5, "getPossibleInstantsFor is not called indefinitely");
|
||||
// Expected calls:
|
||||
// RoundDuration -> MoveRelativeZonedDateTime -> AddZonedDateTime (1)
|
||||
// BalanceTimeDurationRelative ->
|
||||
// AddZonedDateTime (2)
|
||||
// NormalizedTimeDurationToDays ->
|
||||
// AddDaysToZonedDateTime (3, step 12)
|
||||
// AddDaysToZonedDateTime (4, step 15)
|
||||
// AddDaysToZonedDateTime (5, step 18.d)
|
@ -122,25 +122,22 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year, with minimal calendar calls:
|
||||
const expectedMinimalOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// 7.e and 7.g not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateUntil", // 7.o
|
||||
// 7.s not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
// initial AddDate in Duration.p.round 39.c not called because no calendar units
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
|
||||
const instanceMinimal = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 100);
|
||||
instanceMinimal.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
|
||||
assert.compareArray(actual, expectedMinimalOpsForYearRounding, "order of operations with years = 0 and smallestUnit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through Duration.prototype.round that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 9.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 9.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 39.c
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
|
||||
instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
|
||||
@ -149,18 +146,11 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through Duration.prototype.round that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// UnbalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 3.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 3.i
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.c
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.e
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.m
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.q MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 10.d
|
||||
"call options.relativeTo.calendar.dateUntil", // 10.e
|
||||
"call options.relativeTo.calendar.dateAdd", // 39.c
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance2 = new Temporal.Duration(1, 0, 0, 62);
|
||||
instance2.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "months", relativeTo: plainRelativeTo }));
|
||||
@ -169,24 +159,21 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through Duration.prototype.round that rounds to the nearest week:
|
||||
const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// UnbalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 4.e
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateUntil", // 14.f
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 16
|
||||
"call options.relativeTo.calendar.dateUntil", // 17
|
||||
"call options.relativeTo.calendar.dateAdd", // 39.c
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance3 = new Temporal.Duration(1, 1, 0, 15);
|
||||
instance3.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo: plainRelativeTo }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with largestUnit = smallestUnit = weeks");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through UnbalanceDurationRelative that rounds to the nearest day:
|
||||
// code path through Duration.prototype.round that rounds to the nearest day:
|
||||
const expectedOpsForDayRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
"call options.relativeTo.calendar.dateAdd", // 9
|
||||
"call options.relativeTo.calendar.dateAdd", // 39.c
|
||||
]);
|
||||
const instance4 = new Temporal.Duration(1, 1, 1)
|
||||
instance4.round(createOptionsObserver({ largestUnit: "days", smallestUnit: "days", relativeTo: plainRelativeTo }));
|
||||
@ -195,8 +182,8 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through BalanceDateDurationRelative balancing from days up to years:
|
||||
const expectedOpsForDayToYearBalancing = expectedOpsForPlainRelativeTo.concat([
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateUntil", // 9.d
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance5 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 396 * 24);
|
||||
instance5.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "days", relativeTo: plainRelativeTo }));
|
||||
@ -205,13 +192,11 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through Duration.prototype.round balancing from months up to years:
|
||||
const expectedOpsForMonthToYearBalancing = expectedOpsForPlainRelativeTo.concat([
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.c
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.e
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 9.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 9.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 39.c
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance6 = new Temporal.Duration(0, 12);
|
||||
instance6.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "months", relativeTo: plainRelativeTo }));
|
||||
@ -219,8 +204,7 @@ assert.compareArray(actual, expectedOpsForMonthToYearBalancing, "order of operat
|
||||
actual.splice(0); // clear
|
||||
|
||||
const expectedOpsForDayToMonthBalancing = expectedOpsForPlainRelativeTo.concat([
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateUntil", // 10.e
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
]);
|
||||
const instance7 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 32 * 24);
|
||||
instance7.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "days", relativeTo: plainRelativeTo }));
|
||||
@ -228,8 +212,7 @@ assert.compareArray(actual, expectedOpsForDayToMonthBalancing, "order of operati
|
||||
actual.splice(0); // clear
|
||||
|
||||
const expectedOpsForDayToWeekBalancing = expectedOpsForPlainRelativeTo.concat([
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateUntil", // 17
|
||||
"call options.relativeTo.calendar.dateUntil", // DifferencePlainDateTimeWithRounding → DifferenceISODateTime
|
||||
]);
|
||||
const instance8 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 8 * 24);
|
||||
instance8.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "days", relativeTo: plainRelativeTo }));
|
||||
@ -351,18 +334,14 @@ const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.con
|
||||
// lookup in Duration.p.round
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// NanosecondsToDays
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7. GetPlainDateTimeFor
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 11. GetPlainDateTimeFor
|
||||
// NanosecondsToDays → AddDaysToZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// RoundDuration
|
||||
// 7.e and 7.g not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateUntil", // 7.o
|
||||
// 7.s not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
]);
|
||||
instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
@ -372,34 +351,25 @@ assert.compareArray(
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through Duration.p.round that rounds to the nearest year:
|
||||
const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.round
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// NanosecondsToDays
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 8. GetPlainDateTimeFor
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 9. GetPlainDateTimeFor
|
||||
// NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 9.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 9.d
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
]);
|
||||
instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
@ -409,9 +379,8 @@ assert.compareArray(
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path that hits UnbalanceDateDurationRelative, RoundDuration, and
|
||||
// BalanceDateDurationRelative
|
||||
const expectedOpsForUnbalanceRoundBalance = expectedOpsForZonedRelativeTo.concat([
|
||||
// code path that hits the special weeks/years case in BalanceDateDurationRelative
|
||||
const expectedOpsForYearsWeeksSpecialCase = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.round
|
||||
@ -421,26 +390,28 @@ const expectedOpsForUnbalanceRoundBalance = expectedOpsForZonedRelativeTo.concat
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// BalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 10.d
|
||||
"call options.relativeTo.calendar.dateUntil", // 10.e
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
]);
|
||||
new Temporal.Duration(0, 1, 1).round(createOptionsObserver({ largestUnit: "years", smallestUnit: "weeks", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
actual,
|
||||
expectedOpsForUnbalanceRoundBalance,
|
||||
expectedOpsForYearsWeeksSpecialCase,
|
||||
"order of operations with largestUnit = years, smallestUnit = weeks, and ZonedDateTime relativeTo"
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path that skips user code calls in BalanceDateDurationRelative due to
|
||||
// special case for largestUnit months and smallestUnit weeks
|
||||
const expectedOpsForWeeksSpecialCase = expectedOpsForZonedRelativeTo.concat([
|
||||
const expectedOpsForMonthsWeeksSpecialCase = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.round
|
||||
@ -449,16 +420,21 @@ const expectedOpsForWeeksSpecialCase = expectedOpsForZonedRelativeTo.concat([
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
]);
|
||||
new Temporal.Duration(0, 1, 1).round(createOptionsObserver({ largestUnit: "months", smallestUnit: "weeks", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
actual,
|
||||
expectedOpsForWeeksSpecialCase,
|
||||
expectedOpsForMonthsWeeksSpecialCase,
|
||||
"order of operations with largestUnit = months, smallestUnit = weeks, and ZonedDateTime relativeTo"
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
@ -53,4 +53,4 @@ const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuilti
|
||||
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
|
||||
instance.round({ largestUnit: "years", relativeTo });
|
||||
|
||||
assert.sameValue(timeZone.calls, 8, "getPossibleInstantsFor should have been called 8 times");
|
||||
assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");
|
||||
|
@ -1,125 +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.round
|
||||
description: >
|
||||
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||
RangeErrors.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
29. 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 23: 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.round
|
||||
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
|
||||
oneNsDuration.round({
|
||||
relativeTo: zdt,
|
||||
smallestUnit: "days",
|
||||
}),
|
||||
"RangeError when days < 0 and sign = 1"
|
||||
);
|
||||
|
||||
// Step 24: 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.round
|
||||
-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.round({
|
||||
relativeTo: zdt,
|
||||
smallestUnit: "days",
|
||||
}),
|
||||
"RangeError when days > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 26: 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 19, setting _oneDayFarther_
|
||||
],
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Pre-conversion in Duration.p.round
|
||||
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.round({
|
||||
relativeTo: zdt,
|
||||
smallestUnit: "days",
|
||||
}),
|
||||
"RangeError when nanoseconds > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 29: day length is an unsafe integer
|
||||
zdt = new Temporal.ZonedDateTime(
|
||||
0n,
|
||||
timeZoneSubstituteValues(
|
||||
// Not called in step 16 because _days_ = 0
|
||||
// Returned in step 19, 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"
|
||||
);
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-5]],
|
||||
["months", [5, 8], [-5, -7]],
|
||||
["weeks", [5, 6, 9], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -3]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -27]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -16]],
|
||||
["minutes", [5, 7, 0, 27, 16, 31], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 9], [-5, -6, -9]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 31], [-5, -7, 0, -27, -16, -31]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [5], [-6]],
|
||||
["months", [5, 7], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -9]],
|
||||
["weeks", [5, 7, 3], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 27], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 16], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -31]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [6], [-6]],
|
||||
["months", [5, 8], [-5, -8]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 4], [-5, -7, -4]],
|
||||
["days", [5, 7, 0, 28], [-5, -7, 0, -28]],
|
||||
["hours", [5, 7, 0, 27, 17], [-5, -7, 0, -27, -17]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -18,7 +18,7 @@ const relativeToBackwards = new Temporal.PlainDate(2020, 12, 1);
|
||||
const expected = [
|
||||
["years", [5], [-5]],
|
||||
["months", [5, 7], [-5, -7]],
|
||||
["weeks", [5, 6, 8], [-5, -6, -8]],
|
||||
["weeks", [5, 7, 3], [-5, -7, -3]],
|
||||
["days", [5, 7, 0, 27], [-5, -7, 0, -27]],
|
||||
["hours", [5, 7, 0, 27, 16], [-5, -7, 0, -27, -16]],
|
||||
["minutes", [5, 7, 0, 27, 16, 30], [-5, -7, 0, -27, -16, -30]],
|
||||
|
@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.round
|
||||
description: >
|
||||
UnbalanceDurationRelative throws a RangeError when duration signs don't match.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
var duration = Temporal.Duration.from({
|
||||
years: 1,
|
||||
months: 0,
|
||||
weeks: 1,
|
||||
});
|
||||
|
||||
var cal = new class extends Temporal.Calendar {
|
||||
called = 0;
|
||||
dateUntil(one, two, options) {
|
||||
++this.called;
|
||||
var result = super.dateUntil(one, two, options);
|
||||
return this.called === 1 ? result.negated() : result;
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
var relativeTo = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal);
|
||||
assert.sameValue(relativeTo.getCalendar(), cal);
|
||||
|
||||
var options = {
|
||||
smallestUnit: "days",
|
||||
largestUnit: "month",
|
||||
relativeTo,
|
||||
};
|
||||
|
||||
assert.throws(RangeError, () => duration.round(options));
|
@ -34,9 +34,10 @@ features: [Temporal]
|
||||
|
||||
const expected = [
|
||||
"2000-01-01T00:00:00", // called once on the input relativeTo object
|
||||
"2001-02-09T00:00:00", // called once adding the duration to relativeTo
|
||||
"2001-02-09T00:00:00", // called once on relativeTo plus years, months, weeks, days from the receiver
|
||||
"2001-02-10T00:00:00", // called once on the previous value plus the calendar days difference between that and the time part of the duration
|
||||
"2001-02-01T00:00:00", // called once in the balancing step on intermediate relativeTo
|
||||
"2001-02-01T00:00:00", // called to find the lower bound for months rounding
|
||||
"2001-03-01T00:00:00", // called to find the upper bound for months rounding
|
||||
];
|
||||
|
||||
TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
|
||||
|
@ -17,7 +17,7 @@ const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar);
|
||||
// Total of a calendar unit where larger calendar units have to be converted
|
||||
// down, to cover the path that goes through UnbalanceDateDurationRelative
|
||||
// The calls come from the path:
|
||||
// Duration.total() -> UnbalanceDateDurationRelative -> calendar.dateAdd()
|
||||
// Duration.total() -> AddZonedDateTime -> calendar.dateAdd()
|
||||
|
||||
const instance1 = new Temporal.Duration(1, 1, 1, 1, 1);
|
||||
instance1.total({ unit: "days", relativeTo });
|
||||
@ -27,11 +27,9 @@ assert.sameValue(calendar.dateAddCallCount, 1, "converting larger calendar units
|
||||
// up, to cover the path that goes through MoveRelativeZonedDateTime
|
||||
// The calls come from these paths:
|
||||
// Duration.total() ->
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
|
||||
// BalanceDuration ->
|
||||
// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd()
|
||||
// RoundDuration ->
|
||||
// MoveRelativeDate -> calendar.dateAdd()
|
||||
// AddZonedDateTime -> calendar.dateAdd()
|
||||
// DifferenceZonedDateTimeWithRounding -> RoundRelativeDuration -> NudgeToCalendarUnit ->
|
||||
// AddDateTime -> calendar.dateAdd() (2x)
|
||||
|
||||
calendar.dateAddCallCount = 0;
|
||||
|
||||
|
@ -3,38 +3,9 @@
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.total
|
||||
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
|
||||
info: |
|
||||
sec-temporal.duration.prototype.total steps 7–11:
|
||||
7. Let _unbalanceResult_ be ? UnbalanceDateDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_).
|
||||
...
|
||||
10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_).
|
||||
11. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_).
|
||||
sec-temporal-unbalancedatedurationrelative step 3:
|
||||
3. If _largestUnit_ is *"month"*, then
|
||||
...
|
||||
g. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
h. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*).
|
||||
i. Let _untilResult_ be ? CalendarDateUntil(_calendarRec_.[[Receiver]], _plainRelativeTo_, _later_, _untilOptions_, _calendarRec_.[[DateUntil]]).
|
||||
sec-temporal-balanceduration step 3.a:
|
||||
3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
|
||||
sec-temporal-roundduration steps 5.d and 8.n–p:
|
||||
5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
|
||||
...
|
||||
8. If _unit_ is *"year"*, then
|
||||
...
|
||||
n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
|
||||
p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
|
||||
sec-temporal-nanosecondstodays step 11:
|
||||
11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
|
||||
sec-temporal-differenceisodatetime steps 9–11:
|
||||
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
|
||||
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
|
||||
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
|
||||
description: >
|
||||
The options object passed to calendar.dateUntil has a largestUnit property
|
||||
with its value in the singular form
|
||||
includes: [compareArray.js, temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
@ -53,7 +24,7 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
{
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week"],
|
||||
weeks: ["week", "week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
|
@ -15,23 +15,23 @@ const timeZone = TemporalHelpers.springForwardFallBackTimeZone();
|
||||
|
||||
{
|
||||
// Date part of duration lands on skipped DST hour, causing disambiguation
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 12);
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 11, 30);
|
||||
const relativeTo = new Temporal.ZonedDateTime(
|
||||
950868000_000_000_000n /* = 2000-02-18T10Z */,
|
||||
timeZone); /* = 2000-02-18T02-08 in local time */
|
||||
|
||||
assert.sameValue(duration.total({ unit: "months", relativeTo }), 1.5,
|
||||
"1 month 15 days 12 hours should be exactly 1.5 months");
|
||||
"1 month 15 days 11:30 should be exactly 1.5 months");
|
||||
}
|
||||
|
||||
{
|
||||
// Month-only part of duration lands on skipped DST hour, should not cause
|
||||
// disambiguation
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15);
|
||||
const duration = new Temporal.Duration(0, 1, 0, 15, 0, 30);
|
||||
const relativeTo = new Temporal.ZonedDateTime(
|
||||
951991200_000_000_000n /* = 2000-03-02T10Z */,
|
||||
timeZone); /* = 2000-03-02T02-08 in local time */
|
||||
|
||||
assert.sameValue(duration.total({ unit: "months", relativeTo }), 1.5,
|
||||
"1 month 15 days should be exactly 1.5 months");
|
||||
"1 month 15 days 00:30 should be exactly 1.5 months");
|
||||
}
|
||||
|
@ -1,43 +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: >
|
||||
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||
...
|
||||
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||
a. Set _norm_ to _oneDayLess_.
|
||||
b. Set _relativeResult_ to _oneDayFarther_.
|
||||
c. Set _days_ to _days_ + _sign_.
|
||||
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||
i. Throw a *RangeError* exception.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
const duration = Temporal.Duration.from({ days: 1 });
|
||||
|
||||
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 relativeTo = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
|
||||
assert.throws(RangeError, () => duration.total({ unit: "days", relativeTo }), "indefinite loop is prevented");
|
||||
assert.sameValue(calls, 4, "getPossibleInstantsFor is not called indefinitely");
|
||||
// Expected calls:
|
||||
// AddZonedDateTime (1)
|
||||
// NormalizedTimeDurationToDays ->
|
||||
// AddDaysToZonedDateTime (2, step 12)
|
||||
// AddDaysToZonedDateTime (3, step 15)
|
||||
// AddDaysToZonedDateTime (4, step 18.d)
|
@ -105,22 +105,22 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year with minimal calendar calls:
|
||||
const expectedOpsForMinimalYearRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// 12.d and 12.f not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
// 12.r not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// DifferencePlainDateTimeWithRounding -> DifferenceISODateTime
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
instance.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
|
||||
assert.compareArray(actual, expectedOpsForMinimalYearRounding, "order of operations with years = 0 and unit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through Duration.p.total that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // Duration.p.total 19.c
|
||||
// DifferencePlainDateTimeWithRounding
|
||||
"call options.relativeTo.calendar.dateUntil", // 5
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
|
||||
instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
|
||||
@ -129,15 +129,12 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through Duration.prototype.total that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// UnbalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 3.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 3.i
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.c
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.e
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.m
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.q MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // Duration.p.total 19.c
|
||||
// DifferencePlainDateTimeWithRounding
|
||||
"call options.relativeTo.calendar.dateUntil", // 5
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance2 = new Temporal.Duration(1, 0, 0, 62);
|
||||
instance2.total(createOptionsObserver({ unit: "months", relativeTo: plainRelativeTo }));
|
||||
@ -146,12 +143,13 @@ actual.splice(0); // clear
|
||||
|
||||
// code path through Duration.prototype.total that rounds to the nearest week:
|
||||
const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([
|
||||
// UnbalanceDateDurationRelative
|
||||
"call options.relativeTo.calendar.dateAdd", // 4.e
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateUntil", // 14.f
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // Duration.p.total 19.c
|
||||
// DifferencePlainDateTimeWithRounding
|
||||
"call options.relativeTo.calendar.dateUntil", // 5
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
]);
|
||||
const instance3 = new Temporal.Duration(1, 1, 0, 15);
|
||||
instance3.total(createOptionsObserver({ unit: "weeks", relativeTo: plainRelativeTo }));
|
||||
@ -268,28 +266,22 @@ assert.compareArray(actual, expectedOpsForZonedRelativeTo.concat([
|
||||
]), "order of operations for ZonedDateTime relativeTo");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year with minimal calendar operations:
|
||||
// code path through Duration.p.total that rounds to the nearest year with
|
||||
// minimal calendar operations:
|
||||
const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.total
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// BalancePossiblyInfiniteDuration → NanosecondsToDays
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7. GetPlainDateTimeFor
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 11. GetPlainDateTimeFor
|
||||
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// BalancePossiblyInfiniteDuration → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
], [
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// MoveRelativeZonedDateTime → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// 12.d and 12.f not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
// 12.r not called because years, months, weeks are 0
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
]);
|
||||
instance.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
@ -299,32 +291,25 @@ assert.compareArray(
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through Duration.p.total that rounds to years:
|
||||
const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.total
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// 18.c AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 8. GetPlainDateTimeFor
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 9. GetPlainDateTimeFor
|
||||
// BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.d
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.f
|
||||
"call options.relativeTo.calendar.dateUntil", // 12.n
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.r MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
]);
|
||||
instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
@ -334,60 +319,58 @@ assert.compareArray(
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path that hits UnbalanceDateDurationRelative and RoundDuration
|
||||
const expectedOpsForUnbalanceRound = expectedOpsForZonedRelativeTo.concat([
|
||||
// code path through Duration.p.total that rounds to months:
|
||||
const expectedOpsForMonthsRoundingZoned = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.total
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// No user code calls in UnbalanceDateDurationRelative
|
||||
// MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// 18.c AddZonedDateTime
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.c
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.e
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.m
|
||||
"call options.relativeTo.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
]);
|
||||
new Temporal.Duration(0, 1, 1).total(createOptionsObserver({ unit: "months", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
actual,
|
||||
expectedOpsForUnbalanceRound,
|
||||
expectedOpsForMonthsRoundingZoned,
|
||||
"order of operations with unit = months and ZonedDateTime relativeTo"
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path that avoids converting Zoned twice in BalanceTimeDurationRelative
|
||||
const expectedOpsForBalanceRound = expectedOpsForZonedRelativeTo.concat([
|
||||
// code path through Duration.p.total that rounds to weeks:
|
||||
const expectedOpsForWeeksRoundingZoned = expectedOpsForZonedRelativeTo.concat([
|
||||
// ToTemporalDate
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
|
||||
// lookup in Duration.p.total
|
||||
"get options.relativeTo.calendar.dateAdd",
|
||||
"get options.relativeTo.calendar.dateUntil",
|
||||
// No user code calls in UnbalanceDateDurationRelative
|
||||
// No user code calls in AddZonedDateTime (years, months, weeks = 0)
|
||||
// BalanceTimeDurationRelative
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 4.a
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 4.b
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // NanosecondsToDays 9
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // NanosecondsToDays 26
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // NanosecondsToDays 31.a
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 10. GetInstantFor
|
||||
// RoundDuration
|
||||
"call options.relativeTo.calendar.dateUntil", // 14.f
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.j MoveRelativeDate
|
||||
"call options.relativeTo.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// 18.c AddZonedDateTime
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 13. GetInstantFor
|
||||
// DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime
|
||||
"call options.relativeTo.timeZone.getOffsetNanosecondsFor", // 5
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor", // 12.c
|
||||
"call options.relativeTo.calendar.dateUntil", // 13.f
|
||||
// RoundRelativeDuration
|
||||
"call options.relativeTo.calendar.dateUntil",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.calendar.dateAdd",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
"call options.relativeTo.timeZone.getPossibleInstantsFor",
|
||||
]);
|
||||
new Temporal.Duration(0, 0, 0, 1, 240).total(createOptionsObserver({ unit: "weeks", relativeTo: zonedRelativeTo }));
|
||||
assert.compareArray(
|
||||
actual,
|
||||
expectedOpsForBalanceRound,
|
||||
expectedOpsForWeeksRoundingZoned,
|
||||
"order of operations with unit = weeks and no calendar units"
|
||||
);
|
||||
actual.splice(0); // clear
|
||||
|
@ -25,7 +25,7 @@ info: |
|
||||
12. If _unit_ is *"week"*, then
|
||||
...
|
||||
s. Let _fractionalWeeks_ be _weeks_ + _fractionalDays_ / abs(_oneWeekDays_).
|
||||
includes: [compareArray.js]
|
||||
includes: [compareArray.js, temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
@ -68,51 +68,48 @@ function f64Repr(f) {
|
||||
|
||||
// ============
|
||||
|
||||
const tz = new (class extends Temporal.TimeZone {
|
||||
getPossibleInstantsFor() {
|
||||
// Called in NormalizedTimeDurationToDays 19 from RoundDuration 7.b.
|
||||
// Sets _result_.[[DayLength]] to 2⁵³ - 1 ns, its largest possible value
|
||||
return [new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)];
|
||||
}
|
||||
})("UTC");
|
||||
function createTimeZone() {
|
||||
const tz = new Temporal.TimeZone("UTC");
|
||||
TemporalHelpers.substituteMethod(tz, "getPossibleInstantsFor", [
|
||||
TemporalHelpers.SUBSTITUTE_SKIP,
|
||||
[new Temporal.Instant(-86400_0000_0000_000_000_000n)],
|
||||
[new Temporal.Instant(86400_0000_0000_000_000_000n - 100_000_000n)],
|
||||
]);
|
||||
return tz;
|
||||
}
|
||||
|
||||
const cal = new (class extends Temporal.Calendar {
|
||||
dateAdd() {
|
||||
// Called in MoveRelativeDate from RoundDuration 10.x, 11.x, or 12.q.
|
||||
// Sets _oneYearDays_, _oneMonthDays_, or _oneWeekDays_ respectively to
|
||||
// 200_000_000, its largest possible value.
|
||||
return new Temporal.PlainDate(275760, 9, 13);
|
||||
}
|
||||
})("iso8601");
|
||||
function createRelativeTo() {
|
||||
return new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone());
|
||||
}
|
||||
|
||||
const relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, tz, cal);
|
||||
const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, /* nanoseconds = */ 1);
|
||||
|
||||
/*
|
||||
* RoundDuration step 7:
|
||||
* ii. result = { [[Days]] = 0, [[Remainder]] = normalized time duration of 1 ns,
|
||||
* [[DayLength]] = Number.MAX_SAFE_INTEGER }
|
||||
* iii. fractionalDays = 0 + 0 + 1 / Number.MAX_SAFE_INTEGER
|
||||
* step 10:
|
||||
* y. oneYearDays = 200_000_000
|
||||
* z. fractionalYears = 0 + (1 / Number.MAX_SAFE_INTEGER) / 200_000_000
|
||||
*/
|
||||
// NS_MAX_INSTANT = 86400 × 1e8 × 1e9
|
||||
// startEpochNs = -NS_MAX_INSTANT
|
||||
// destEpochNs = -NS_MAX_INSTANT + 1
|
||||
// endEpochNs = NS_MAX_INSTANT - 1e8
|
||||
// progress = (destEpochNs - startEpochNs) / (endEpochNs - startEpochNs)
|
||||
// = 1 / (2 × NS_MAX_INSTANT - 1e8)
|
||||
// total = startDuration.years + progress = 0 + 1 / (2 × NS_MAX_INSTANT - 1e8)
|
||||
//
|
||||
// Calculated with Python's Decimal module to 50 decimal places
|
||||
const expected = 5.55111512312578_3318415740544369642963189519987393e-25;
|
||||
const expected = 5.7870370370370_705268347050756396228860247432848547e-23;
|
||||
// This is float 5.7870370370370_6998177e-23
|
||||
// Note: if calculated using floating point arithmetic, this will give the less
|
||||
// precise value 5.7870370370370_7115726e-23
|
||||
|
||||
// Check that we are not accidentally losing precision in our expected value:
|
||||
|
||||
assert.sameValue(expected, 5.55111512312578_373662e-25, "the float representation of the result is 5.55111512312578373662e-25");
|
||||
assert.sameValue(expected, 5.7870370370370_6998177e-23, "the float representation of the result is 5.7870370370370_6998177e-23");
|
||||
assert.compareArray(
|
||||
f64Repr(expected),
|
||||
[0x3a, 0xe5, 0x79, 0x8e, 0xe2, 0x30, 0x8c, 0x3b],
|
||||
"the bit representation of the result is 0x3ae5798ee2308c3b"
|
||||
[0x3b, 0x51, 0x7d, 0x80, 0xc6, 0xf1, 0x14, 0xa8],
|
||||
"the bit representation of the result is 0x3b517d80c6f114a8"
|
||||
);
|
||||
// The next Number in direction -Infinity is less precise.
|
||||
assert.sameValue(nextDown(expected), 5.55111512312578_281826e-25, "the next Number in direction -Infinity is less precise");
|
||||
assert.sameValue(nextDown(expected), 5.7870370370370_6880628e-23, "the next Number in direction -Infinity is less precise");
|
||||
// The next Number in direction +Infinity is less precise.
|
||||
assert.sameValue(nextUp(expected), 5.55111512312578_465497e-25, "the next Number in direction +Infinity is less precise");
|
||||
assert.sameValue(nextUp(expected), 5.7870370370370_7115727e-23, "the next Number in direction +Infinity is less precise");
|
||||
|
||||
assert.sameValue(d.total({ unit: "years", relativeTo }), expected, "Correct division by large number in years total");
|
||||
assert.sameValue(d.total({ unit: "months", relativeTo }), expected, "Correct division by large number in months total");
|
||||
assert.sameValue(d.total({ unit: "weeks", relativeTo }), expected, "Correct division by large number in weeks total");
|
||||
assert.sameValue(d.total({ unit: "years", relativeTo: createRelativeTo() }), expected, "Correct division by large number in years total");
|
||||
assert.sameValue(d.total({ unit: "months", relativeTo: createRelativeTo() }), expected, "Correct division by large number in months total");
|
||||
assert.sameValue(d.total({ unit: "weeks", relativeTo: createRelativeTo() }), expected, "Correct division by large number in weeks total");
|
||||
|
@ -67,86 +67,57 @@ function f64Repr(f) {
|
||||
}
|
||||
|
||||
// ============
|
||||
// Set up contrived custom time zone and calendar to give dayLengthNs,
|
||||
// oneYearDays, oneMonthDays, and oneWeekDays their largest possible values
|
||||
// Set up contrived custom time zone to give the denominator of the fraction its
|
||||
// largest possible value
|
||||
|
||||
function createTimeZone() {
|
||||
const tz = new Temporal.TimeZone("UTC");
|
||||
TemporalHelpers.substituteMethod(tz, "getPossibleInstantsFor", [
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.e.ii AddDaysToZonedDateTime
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.i.ii NormalizedTimeDurationToDays step 16
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.i.ii NormalizedTimeDurationToDays step 19
|
||||
[new Temporal.Instant(-86400_0000_0000_000_000_000n)], // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
[new Temporal.Instant(-86400_0000_0000_000_000_000n + 2n ** 53n - 1n)], // RoundDuration step 7.a.ii NormalizedTimeDurationToDays step 19
|
||||
// sets dayLengthNs to Number.MAX_SAFE_INTEGER
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 18.c AddZonedDateTime
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // DifferenceZonedDateTimeWithRounding → DifferenceZonedDateTime → AddZonedDateTime
|
||||
[new Temporal.Instant(-86400_0000_0000_000_000_000n)],
|
||||
[new Temporal.Instant(86400_0000_0000_000_000_000n)],
|
||||
]);
|
||||
return tz;
|
||||
}
|
||||
|
||||
function createCalendar() {
|
||||
const cal = new Temporal.Calendar("iso8601");
|
||||
TemporalHelpers.substituteMethod(cal, "dateAdd", [
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
new Temporal.PlainDate(-271821, 4, 20), // RoundDuration step 10.d/11.d AddDate
|
||||
new Temporal.PlainDate(-271821, 4, 20), // RoundDuration step 10.f/11.f AddDate
|
||||
new Temporal.PlainDate(275760, 9, 13), // RoundDuration step 10.r/11.r MoveRelativeDate
|
||||
// sets one{Year,Month,Week}Days to 200_000_000
|
||||
]);
|
||||
return cal;
|
||||
function createRelativeTo() {
|
||||
return new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone());
|
||||
}
|
||||
|
||||
// ============
|
||||
|
||||
// We will calculate the total years/months/weeks of durations with:
|
||||
// 1 year/month/week, 1 day, 0.199000001 s
|
||||
// We will calculate the total years of a duration of 1 year, 0.009254648 s
|
||||
|
||||
// RoundDuration step 7:
|
||||
// ii. result = { [[Days]] = 1, [[Remainder]] = normalized time duration of 199_000_001 ns,
|
||||
// [[DayLength]] = Number.MAX_SAFE_INTEGER }
|
||||
// iii. fractionalDays = 1 + 0 + 199_000_001 / Number.MAX_SAFE_INTEGER
|
||||
// step 10: (similar for steps 11 and 12 in the case of months/weeks)
|
||||
// y. oneYearDays = 200_000_000
|
||||
// z. fractionalYears = 1 + (1 + 199_000_001 / Number.MAX_SAFE_INTEGER) / 200_000_000
|
||||
// NS_MAX_INSTANT = 86400 × 1e8 × 1e9
|
||||
// startEpochNs = -NS_MAX_INSTANT
|
||||
// destEpochNs = -NS_MAX_INSTANT + 366 * 86400 * 1e9 + 9254648
|
||||
// endEpochNs = NS_MAX_INSTANT
|
||||
// progress = (destEpochNs - startEpochNs) / (endEpochNs - startEpochNs)
|
||||
// = (366 * 86400 * 1e9 + 9254648) / (2 × NS_MAX_INSTANT)
|
||||
// total = startDuration.years + progress
|
||||
// = 1 + (366 * 86400 * 1e9 + 9254648) / (2 × NS_MAX_INSTANT)
|
||||
//
|
||||
// Note: if calculated as 1 + 1/200_000_000 + 199_000_001 / Number.MAX_SAFE_INTEGER / 200_000_000
|
||||
// this will lose precision and give the result 1.000000005.
|
||||
|
||||
// Calculated with Python's Decimal module to 50 decimal places
|
||||
const expected = 1.000000005000000_1104671915053146003490515686745299;
|
||||
const expected = 1.000001830000000_5355699074074074074074074074074074;
|
||||
// This is float 1.000001830000000_64659
|
||||
// Note: if calculated using floating point arithmetic, this will give the less
|
||||
// precise value 1.000001830000000_42454
|
||||
|
||||
// Check that we are not accidentally losing precision in our expected value:
|
||||
|
||||
assert.sameValue(expected, 1.000000005000000_2, "the float representation of the result is 1.0000000050000002");
|
||||
assert.sameValue(expected, 1.000001830000000_64659, "the float representation of the result is 1.00000183000000064659");
|
||||
assert.compareArray(
|
||||
f64Repr(expected),
|
||||
[0x3f, 0xf0, 0x00, 0x00, 0x01, 0x57, 0x98, 0xef],
|
||||
"the bit representation of the result is 0x3ff00000015798ef"
|
||||
[0x3f, 0xf0, 0x00, 0x01, 0xeb, 0x3c, 0xa4, 0x79],
|
||||
"the bit representation of the result is 0x3ff00001eb3ca479"
|
||||
);
|
||||
// The next Number in direction -Infinity is less precise.
|
||||
assert.sameValue(nextDown(expected), 1.000000004999999_96961, "the next Number in direction -Infinity is less precise");
|
||||
assert.sameValue(nextDown(expected), 1.000001830000000_42455, "the next Number in direction -Infinity is less precise");
|
||||
// The next Number in direction +Infinity is less precise.
|
||||
assert.sameValue(nextUp(expected), 1.000000005000000_4137, "the next Number in direction +Infinity is less precise");
|
||||
assert.sameValue(nextUp(expected), 1.000001830000000_86864, "the next Number in direction +Infinity is less precise");
|
||||
|
||||
// ============
|
||||
|
||||
let relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), createCalendar());
|
||||
const dYears = new Temporal.Duration(/* years = */ 1, 0, 0, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
|
||||
assert.sameValue(dYears.total({ unit: "years", relativeTo }), expected, "Correct division by large number in years total");
|
||||
|
||||
relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), createCalendar());
|
||||
const dMonths = new Temporal.Duration(0, /* months = */ 1, 0, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
|
||||
assert.sameValue(dMonths.total({ unit: "months", relativeTo }), expected, "Correct division by large number in months total");
|
||||
|
||||
// Weeks calculation doesn't have the AddDate calls to convert months/weeks to days
|
||||
const weeksCal = new Temporal.Calendar("iso8601");
|
||||
TemporalHelpers.substituteMethod(weeksCal, "dateAdd", [
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Duration.total step 19.a MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // RoundDuration step 7.a.i MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
new Temporal.PlainDate(275760, 9, 13), // RoundDuration step 12.q MoveRelativeDate
|
||||
// sets one{Year,Month,Week}Days to 200_000_000
|
||||
]);
|
||||
relativeTo = new Temporal.ZonedDateTime(-86400_0000_0000_000_000_000n, createTimeZone(), weeksCal);
|
||||
const dWeeks = new Temporal.Duration(0, 0, /* weejs = */ 1, /* days = */ 1, 0, 0, 0, /* milliseconds = */ 199, 0, /* nanoseconds = */ 1);
|
||||
assert.sameValue(dWeeks.total({ unit: "weeks", relativeTo }), expected, "Correct division by large number in weeks total");
|
||||
const duration = new Temporal.Duration(/* years = */ 1, 0, 0, 0, 0, 0, 0, 9, 254, 648);
|
||||
assert.sameValue(duration.total({ unit: "years", relativeTo: createRelativeTo() }), expected, "Correct division by large number in years total");
|
||||
|
@ -1,125 +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: >
|
||||
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||
RangeErrors.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
29. 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 23: 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 24: 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 26: 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 19, 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 29: day length is an unsafe integer
|
||||
zdt = new Temporal.ZonedDateTime(
|
||||
0n,
|
||||
timeZoneSubstituteValues(
|
||||
// Not called in step 16 because _days_ = 0
|
||||
// Returned in step 19, 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,17 +0,0 @@
|
||||
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.plaindate.prototype.since
|
||||
description: >
|
||||
relativeTo parameters that are not ZonedDateTime or undefined, are always
|
||||
converted to PlainDate for observable calendar calls
|
||||
includes: [temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
|
||||
const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
|
||||
calendar.specificPlainDate = instance;
|
||||
instance.since(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
|
||||
assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
|
@ -125,22 +125,16 @@ instance.since(identicalPropertyBag, createOptionsObserver());
|
||||
assert.compareArray(actual, expected, "order of operations with identical dates");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expected.concat([
|
||||
// lookup
|
||||
"get this.calendar.dateAdd",
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
@ -160,14 +154,9 @@ const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.n not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
|
||||
@ -180,13 +169,9 @@ const expectedOpsForMonthRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil" // 10.e
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
@ -199,12 +184,10 @@ const expectedOpsForWeekRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil" // 17
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.plaindate.prototype.until
|
||||
description: >
|
||||
relativeTo parameters that are not ZonedDateTime or undefined, are always
|
||||
converted to PlainDate for observable calendar calls
|
||||
includes: [temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
|
||||
const instance = new Temporal.PlainDate(1970, 1, 1, calendar);
|
||||
calendar.specificPlainDate = instance;
|
||||
instance.until(new Temporal.PlainDate(2000, 5, 2, calendar), { smallestUnit: "month" });
|
||||
assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");
|
@ -133,15 +133,9 @@ const expectedOpsForYearRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
@ -161,14 +155,9 @@ const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.n not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
|
||||
@ -181,13 +170,9 @@ const expectedOpsForMonthRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil" // 10.e
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
@ -200,12 +185,10 @@ const expectedOpsForWeekRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil" // 17
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
|
@ -162,15 +162,9 @@ const expectedOpsForYearRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
@ -196,14 +190,9 @@ const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.n not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
|
||||
@ -216,14 +205,9 @@ const expectedOpsForMonthRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateUntil", // 13.m
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil", // 10.e
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
@ -236,12 +220,10 @@ const expectedOpsForWeekRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil", // 17
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
|
@ -12,8 +12,8 @@ features: [Temporal]
|
||||
// Test case provided by André Bargull
|
||||
|
||||
const cal = new (class extends Temporal.Calendar {
|
||||
dateAdd(date, duration, options) {
|
||||
return super.dateAdd(date, duration.negated(), options);
|
||||
dateUntil(date, duration, options) {
|
||||
return super.dateUntil(date, duration, options).negated();
|
||||
}
|
||||
})("iso8601");
|
||||
|
||||
|
@ -162,15 +162,9 @@ const expectedOpsForYearRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
@ -196,14 +190,9 @@ const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.n not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDatePropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months/weeks");
|
||||
@ -216,14 +205,9 @@ const expectedOpsForMonthRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateUntil", // 13.m
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil" // 10.e
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = years");
|
||||
@ -236,12 +220,10 @@ const expectedOpsForWeekRounding = expected.concat([
|
||||
"get this.calendar.dateUntil",
|
||||
// CalendarDateUntil
|
||||
"call this.calendar.dateUntil",
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil" // 17
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
|
@ -12,8 +12,8 @@ features: [Temporal]
|
||||
// Test case provided by André Bargull
|
||||
|
||||
const cal = new (class extends Temporal.Calendar {
|
||||
dateAdd(date, duration, options) {
|
||||
return super.dateAdd(date, duration.negated(), options);
|
||||
dateUntil(date, duration, options) {
|
||||
return super.dateUntil(date, duration, options).negated();
|
||||
}
|
||||
})("iso8601");
|
||||
|
||||
|
@ -138,23 +138,16 @@ instance.since(identicalPropertyBag, createOptionsObserver());
|
||||
assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expected.concat([
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 7.e
|
||||
"call this.calendar.dateAdd", // 7.g
|
||||
"call this.calendar.dateUntil", // 7.o
|
||||
"call this.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
// (7.s not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year and skips a DateUntil call
|
||||
const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
|
||||
year: 2001,
|
||||
month: 5,
|
||||
@ -162,28 +155,18 @@ const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(a
|
||||
calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
|
||||
}, "other");
|
||||
const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"call this.calendar.dateAdd", // 7.e
|
||||
"call this.calendar.dateAdd", // 7.g
|
||||
"call this.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
// (7.o not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.since(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest month:
|
||||
// code path through RoundRelativeDuration that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expected.concat([
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 10.c
|
||||
"call this.calendar.dateAdd", // 10.e
|
||||
"call this.calendar.dateAdd", // 10.k MoveRelativeDate
|
||||
// (10.n.iii MoveRelativeDate not called because weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil" // 10.e
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd", // BubbleRelativeDuration
|
||||
]);
|
||||
instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
|
@ -138,23 +138,16 @@ instance.until(identicalPropertyBag, createOptionsObserver());
|
||||
assert.compareArray(actual, expectedMinimal, "order of operations with identical year-months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expected.concat([
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 7.e
|
||||
"call this.calendar.dateAdd", // 7.g
|
||||
"call this.calendar.dateUntil", // 7.o
|
||||
"call this.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
// (7.s not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year and skips a DateUntil call
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year and skips a DateUntil call
|
||||
const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(actual, {
|
||||
year: 2001,
|
||||
month: 5,
|
||||
@ -162,28 +155,18 @@ const otherYearMonthPropertyBagSameMonth = TemporalHelpers.propertyBagObserver(a
|
||||
calendar: TemporalHelpers.calendarObserver(actual, "other.calendar"),
|
||||
}, "other");
|
||||
const expectedOpsForYearRoundingSameMonth = expected.concat([
|
||||
"call this.calendar.dateAdd", // 7.e
|
||||
"call this.calendar.dateAdd", // 7.g
|
||||
"call this.calendar.dateAdd", // 7.y MoveRelativeDate
|
||||
// (7.o not called because months and weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
]);
|
||||
instance.until(otherYearMonthPropertyBagSameMonth, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRoundingSameMonth, "order of operations with smallestUnit = years and no excess months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest month:
|
||||
// code path through RoundRelativeDuration that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expected.concat([
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 10.c
|
||||
"call this.calendar.dateAdd", // 10.e
|
||||
"call this.calendar.dateAdd", // 10.k MoveRelativeDate
|
||||
// (10.n.iii MoveRelativeDate not called because weeks == 0)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil" // 10.e
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd", // BubbleRelativeDuration
|
||||
]);
|
||||
instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "months", roundingIncrement: 2 }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
|
@ -17,11 +17,8 @@ const later = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calen
|
||||
|
||||
// Difference with rounding, with smallestUnit a calendar unit.
|
||||
// The calls come from these paths:
|
||||
// ZonedDateTime.since() ->
|
||||
// RoundDuration ->
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
// MoveRelativeDate -> calendar.dateAdd()
|
||||
// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
|
||||
// ZonedDateTime.since() -> DifferenceZonedDateTimeWithRounding -> RoundRelativeDuration -> NudgeToCalendarUnit ->
|
||||
// AddDateTime -> calendar.dateAdd()
|
||||
|
||||
later.since(earlier, { smallestUnit: "weeks" });
|
||||
assert.sameValue(calendar.dateAddCallCount, 3, "rounding difference with calendar smallestUnit");
|
||||
assert.sameValue(calendar.dateAddCallCount, 2, "rounding difference with calendar smallestUnit");
|
||||
|
@ -3,47 +3,9 @@
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.zoneddatetime.prototype.since
|
||||
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
|
||||
info: |
|
||||
sec-temporal.zoneddatetime.prototype.since steps 14–18:
|
||||
14. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
c. Return ...
|
||||
15. ...
|
||||
16. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
|
||||
17. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
|
||||
18. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
|
||||
sec-temporal-differencezoneddatetime steps 7 and 11:
|
||||
7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
|
||||
11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
|
||||
sec-temporal-roundduration steps 5.d and 8.n–p:
|
||||
5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
|
||||
...
|
||||
8. If _unit_ is *"year"*, then
|
||||
...
|
||||
n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
|
||||
p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
|
||||
sec-temporal-adjustroundeddurationdays steps 1 and 9:
|
||||
1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
|
||||
a. Return ...
|
||||
...
|
||||
9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
|
||||
sec-temporal-addduration step 7.a–g:
|
||||
a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
|
||||
...
|
||||
f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
g. Else,
|
||||
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
|
||||
sec-temporal-nanosecondstodays step 11:
|
||||
11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
|
||||
sec-temporal-differenceisodatetime steps 9–11:
|
||||
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
|
||||
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
|
||||
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
|
||||
description: >
|
||||
The options object passed to calendar.dateUntil has a largestUnit property
|
||||
with its value in the singular form
|
||||
includes: [compareArray.js, temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
@ -77,9 +39,9 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
later.since(earlier, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
|
||||
},
|
||||
{
|
||||
years: ["year", "year", "year"],
|
||||
months: ["month", "month", "month"],
|
||||
weeks: ["week", "week", "week"],
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
@ -100,9 +62,9 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
later.since(earlier, { smallestUnit });
|
||||
},
|
||||
{
|
||||
years: ["year", "year", "year"],
|
||||
months: ["month", "month", "month"],
|
||||
weeks: ["week", "week", "week"],
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week", "week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
|
@ -23,14 +23,8 @@ features: [Temporal]
|
||||
// Based on a test case by André Bargull
|
||||
|
||||
const calendar = new class extends Temporal.Calendar {
|
||||
#dateUntil = 0;
|
||||
|
||||
dateUntil(one, two, options) {
|
||||
let result = super.dateUntil(one, two, options);
|
||||
if (++this.#dateUntil === 2) {
|
||||
result = result.negated();
|
||||
}
|
||||
return result;
|
||||
return super.dateUntil(one, two, options).negated();
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
|
@ -1,43 +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: >
|
||||
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||
...
|
||||
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||
a. Set _norm_ to _oneDayLess_.
|
||||
b. Set _relativeResult_ to _oneDayFarther_.
|
||||
c. Set _days_ to _days_ + _sign_.
|
||||
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||
i. Throw a *RangeError* exception.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
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 * 2n, "UTC", "iso8601");
|
||||
|
||||
assert.throws(RangeError, () => zdt.since(other, { largestUnit: "day", smallestUnit: "second" }), "indefinite loop is prevented");
|
||||
assert.sameValue(calls, 4, "getPossibleInstantsFor is not called indefinitely");
|
||||
// Expected calls:
|
||||
// DifferenceTemporalZonedDateTime ->
|
||||
// DifferenceZonedDateTime -> GetInstantFor (1)
|
||||
// NormalizedTimeDurationToDays ->
|
||||
// AddDaysToZonedDateTime (2, step 12)
|
||||
// AddDaysToZonedDateTime (3, step 15)
|
||||
// AddDaysToZonedDateTime (4, step 18.d)
|
@ -1,138 +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: >
|
||||
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||
RangeErrors.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
29. 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", smallestUnit: "seconds", roundingMode: "expand" };
|
||||
|
||||
// Step 23: days < 0 and sign = 1
|
||||
let start = new Temporal.ZonedDateTime(
|
||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[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
|
||||
),
|
||||
"days < 0 and sign = 1"
|
||||
);
|
||||
|
||||
// Step 24: days > 0 and sign = -1
|
||||
start = new Temporal.ZonedDateTime(
|
||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[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
|
||||
),
|
||||
"days > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 26: nanoseconds > 0 and sign = -1
|
||||
start = new Temporal.ZonedDateTime(
|
||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[new Temporal.Instant(-2_000_000_000n)], // 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
|
||||
),
|
||||
"norm > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 29: day length is an unsafe integer
|
||||
start = new Temporal.ZonedDateTime(
|
||||
0n,
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
// Not called in step 16 because _days_ = 0
|
||||
// Returned in step 19, 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"
|
||||
);
|
@ -327,64 +327,39 @@ const expectedOpsForCalendarDifference = [
|
||||
"call this.calendar.dateUntil",
|
||||
];
|
||||
|
||||
const expectedOpsForCalendarRounding = [
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
const expectedOpsForCalendarRounding = expected.concat(expectedOpsForCalendarDifference, [
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
// RoundDuration → NanosecondsToDays
|
||||
"call this.timeZone.getOffsetNanosecondsFor",
|
||||
"call this.timeZone.getOffsetNanosecondsFor",
|
||||
// RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
];
|
||||
]);
|
||||
|
||||
// code path that skips RoundDuration:
|
||||
// code path that skips RoundRelativeDuration:
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 }));
|
||||
assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year:
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
assert.compareArray(actual, expectedOpsForCalendarRounding, "order of operations with smallestUnit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateUntil", // 13.m
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil", // 10.e
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest month:
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
assert.compareArray(actual, expectedOpsForCalendarRounding, "order of operations with smallestUnit = months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest week:
|
||||
const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil", // 17
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest week:
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference, [
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
]), "order of operations with smallestUnit = weeks");
|
||||
actual.splice(0); // clear
|
||||
|
||||
instance.since(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" }));
|
||||
|
@ -20,5 +20,3 @@ const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
|
||||
const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
|
||||
const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
|
||||
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
|
||||
const result2 = later.since(earlier, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
|
||||
TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
|
||||
|
@ -17,11 +17,8 @@ const later = new Temporal.ZonedDateTime(1_213_200_000_000_000n, timeZone, calen
|
||||
|
||||
// Difference with rounding, with smallestUnit a calendar unit.
|
||||
// The calls come from these paths:
|
||||
// ZonedDateTime.until() ->
|
||||
// RoundDuration ->
|
||||
// MoveRelativeZonedDateTime -> AddZonedDateTime -> calendar.dateAdd()
|
||||
// MoveRelativeDate -> calendar.dateAdd()
|
||||
// BalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd()
|
||||
// ZonedDateTime.until() -> DifferenceZonedDateTimeWithRounding -> RoundRelativeDuration -> NudgeToCalendarUnit ->
|
||||
// AddDateTime -> calendar.dateAdd() (2x)
|
||||
|
||||
earlier.until(later, { smallestUnit: "weeks" });
|
||||
assert.sameValue(calendar.dateAddCallCount, 3, "rounding difference with calendar smallestUnit");
|
||||
assert.sameValue(calendar.dateAddCallCount, 2, "rounding difference with calendar smallestUnit");
|
||||
|
@ -3,47 +3,9 @@
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.zoneddatetime.prototype.until
|
||||
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
|
||||
info: |
|
||||
sec-temporal.zoneddatetime.prototype.until steps 13–17:
|
||||
13. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
c. Return ...
|
||||
14. ...
|
||||
15. Let _difference_ be ? DifferenceZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _other_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _largestUnit_).
|
||||
16. Let _roundResult_ be ? RoundDuration(_difference_.[[Years]], _difference_.[[Months]], _difference_.[[Weeks]], _difference_.[[Days]], _difference_.[[Hours]], _difference_.[[Minutes]], _difference_.[[Seconds]], _difference_.[[Milliseconds]], _difference_.[[Microseconds]], _difference_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
|
||||
17. Let _result_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _zonedDateTime_).
|
||||
sec-temporal-differencezoneddatetime steps 7 and 11:
|
||||
7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_).
|
||||
11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_).
|
||||
sec-temporal-roundduration steps 5.d and 8.n–p:
|
||||
5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_).
|
||||
...
|
||||
8. If _unit_ is *"year"*, then
|
||||
...
|
||||
n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*).
|
||||
o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*).
|
||||
p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_)
|
||||
sec-temporal-adjustroundeddurationdays steps 1 and 9:
|
||||
1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then
|
||||
a. Return ...
|
||||
...
|
||||
9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_).
|
||||
sec-temporal-addduration step 7.a–g:
|
||||
a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot.
|
||||
...
|
||||
f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then
|
||||
...
|
||||
g. Else,
|
||||
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
|
||||
sec-temporal-nanosecondstodays step 11:
|
||||
11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*).
|
||||
sec-temporal-differenceisodatetime steps 9–11:
|
||||
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
|
||||
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
|
||||
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
|
||||
description: >
|
||||
The options object passed to calendar.dateUntil has a largestUnit property
|
||||
with its value in the singular form
|
||||
includes: [compareArray.js, temporalHelpers.js]
|
||||
features: [Temporal]
|
||||
---*/
|
||||
@ -77,9 +39,9 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
earlier.until(later, { largestUnit, roundingIncrement: 2, roundingMode: 'ceil' });
|
||||
},
|
||||
{
|
||||
years: ["year", "year", "year"],
|
||||
months: ["month", "month", "month"],
|
||||
weeks: ["week", "week", "week"],
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
@ -100,9 +62,9 @@ TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
|
||||
earlier.until(later, { smallestUnit });
|
||||
},
|
||||
{
|
||||
years: ["year", "year", "year"],
|
||||
months: ["month", "month", "month"],
|
||||
weeks: ["week", "week", "week"],
|
||||
years: ["year"],
|
||||
months: ["month"],
|
||||
weeks: ["week", "week"],
|
||||
days: [],
|
||||
hours: [],
|
||||
minutes: [],
|
||||
|
@ -23,14 +23,8 @@ features: [Temporal]
|
||||
// Based on a test case by André Bargull
|
||||
|
||||
const calendar = new class extends Temporal.Calendar {
|
||||
#dateUntil = 0;
|
||||
|
||||
dateUntil(one, two, options) {
|
||||
let result = super.dateUntil(one, two, options);
|
||||
if (++this.#dateUntil === 2) {
|
||||
result = result.negated();
|
||||
}
|
||||
return result;
|
||||
return super.dateUntil(one, two, options).negated();
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
|
@ -1,43 +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: >
|
||||
NormalizedTimeDurationToDays should not be able to loop arbitrarily.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDatetime ] )
|
||||
...
|
||||
22. If NormalizedTimeDurationSign(_oneDayLess_) × _sign_ ≥ 0, then
|
||||
a. Set _norm_ to _oneDayLess_.
|
||||
b. Set _relativeResult_ to _oneDayFarther_.
|
||||
c. Set _days_ to _days_ + _sign_.
|
||||
d. Set _oneDayFarther_ to ? AddDaysToZonedDateTime(_relativeResult_.[[Instant]], _relativeResult_.[[DateTime]], _timeZoneRec_, _zonedRelativeTo_.[[Calendar]], _sign_).
|
||||
e. Set dayLengthNs to NormalizedTimeDurationFromEpochNanosecondsDifference(_oneDayFarther.[[EpochNanoseconds]], relativeResult.[[EpochNanoseconds]]).
|
||||
f. If NormalizedTimeDurationSign(? SubtractNormalizedTimeDuration(_norm_, _dayLengthNs_)) × _sign_ ≥ 0, then
|
||||
i. Throw a *RangeError* exception.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
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 * 2n, "UTC", "iso8601");
|
||||
|
||||
assert.throws(RangeError, () => zdt.until(other, { largestUnit: "day", smallestUnit: "second" }), "indefinite loop is prevented");
|
||||
assert.sameValue(calls, 4, "getPossibleInstantsFor is not called indefinitely");
|
||||
// Expected calls:
|
||||
// DifferenceTemporalZonedDateTime ->
|
||||
// DifferenceZonedDateTime -> GetInstantFor (1)
|
||||
// NormalizedTimeDurationToDays ->
|
||||
// AddDaysToZonedDateTime (2, step 12)
|
||||
// AddDaysToZonedDateTime (3, step 15)
|
||||
// AddDaysToZonedDateTime (4, step 18.d)
|
@ -1,138 +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: >
|
||||
Abstract operation NormalizedTimeDurationToDays can throw four different
|
||||
RangeErrors.
|
||||
info: |
|
||||
NormalizedTimeDurationToDays ( norm, zonedRelativeTo, timeZoneRec [ , precalculatedPlainDateTime ] )
|
||||
23. If days < 0 and sign = 1, throw a RangeError exception.
|
||||
24. If days > 0 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
26. If NormalizedTimeDurationSign(_norm_) = 1 and sign = -1, throw a RangeError exception.
|
||||
...
|
||||
29. 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", smallestUnit: "seconds", roundingMode: "expand" };
|
||||
|
||||
// Step 23: days < 0 and sign = 1
|
||||
let start = new Temporal.ZonedDateTime(
|
||||
0n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[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
|
||||
),
|
||||
"days < 0 and sign = 1"
|
||||
);
|
||||
|
||||
// Step 24: days > 0 and sign = -1
|
||||
start = new Temporal.ZonedDateTime(
|
||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[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
|
||||
),
|
||||
"days > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 26: nanoseconds > 0 and sign = -1
|
||||
start = new Temporal.ZonedDateTime(
|
||||
1n, // Sets DifferenceZonedDateTime _ns1_
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
[new Temporal.Instant(-2_000_000_000n)], // 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
|
||||
),
|
||||
"norm > 0 and sign = -1"
|
||||
);
|
||||
|
||||
// Step 29: day length is an unsafe integer
|
||||
start = new Temporal.ZonedDateTime(
|
||||
0n,
|
||||
timeZoneSubstituteValues(
|
||||
[
|
||||
TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally in DifferenceZonedDateTime
|
||||
// Not called in step 16 because _days_ = 0
|
||||
// Returned in step 19, 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"
|
||||
);
|
@ -327,64 +327,39 @@ const expectedOpsForCalendarDifference = [
|
||||
"call this.calendar.dateUntil",
|
||||
];
|
||||
|
||||
const expectedOpsForCalendarRounding = [
|
||||
// RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
|
||||
const expectedOpsForCalendarRounding = expected.concat(expectedOpsForCalendarDifference, [
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
// RoundDuration → NanosecondsToDays
|
||||
"call this.timeZone.getOffsetNanosecondsFor",
|
||||
"call this.timeZone.getOffsetNanosecondsFor",
|
||||
// RoundDuration → NanosecondsToDays → AddDaysToZonedDateTime
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
];
|
||||
]);
|
||||
|
||||
// code path that skips RoundDuration:
|
||||
// code path that skips RoundRelativeDuration:
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "years", smallestUnit: "nanoseconds", roundingIncrement: 1 }));
|
||||
assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference), "order of operations with largestUnit years and no rounding");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest year:
|
||||
const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 12.d
|
||||
"call this.calendar.dateAdd", // 12.f
|
||||
"call this.calendar.dateUntil", // 12.n
|
||||
"call this.calendar.dateAdd", // 12.x MoveRelativeDate
|
||||
// (12.r not called because other units can't add up to >1 year at this point)
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 9.c
|
||||
"call this.calendar.dateUntil" // 9.d
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest year:
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
|
||||
assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
|
||||
assert.compareArray(actual, expectedOpsForCalendarRounding, "order of operations with smallestUnit = years");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest month:
|
||||
const expectedOpsForMonthRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateAdd", // 13.c
|
||||
"call this.calendar.dateAdd", // 13.e
|
||||
"call this.calendar.dateUntil", // 13.m
|
||||
"call this.calendar.dateAdd", // 13.w MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 10.d
|
||||
"call this.calendar.dateUntil", // 10.e
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest month:
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "months" }));
|
||||
assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with smallestUnit = months");
|
||||
assert.compareArray(actual, expectedOpsForCalendarRounding, "order of operations with smallestUnit = months");
|
||||
actual.splice(0); // clear
|
||||
|
||||
// code path through RoundDuration that rounds to the nearest week:
|
||||
const expectedOpsForWeekRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
|
||||
// RoundDuration
|
||||
"call this.calendar.dateUntil", // 14.f
|
||||
"call this.calendar.dateAdd", // 14.p MoveRelativeDate
|
||||
// BalanceDateDurationRelative
|
||||
"call this.calendar.dateAdd", // 16
|
||||
"call this.calendar.dateUntil", // 17
|
||||
]);
|
||||
// code path through RoundRelativeDuration that rounds to the nearest week:
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "weeks" }));
|
||||
assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with smallestUnit = weeks");
|
||||
assert.compareArray(actual, expected.concat(expectedOpsForCalendarDifference, [
|
||||
// RoundRelativeDuration
|
||||
"call this.calendar.dateUntil",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.calendar.dateAdd",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
"call this.timeZone.getPossibleInstantsFor",
|
||||
]), "order of operations with smallestUnit = weeks");
|
||||
actual.splice(0); // clear
|
||||
|
||||
instance.until(otherDateTimePropertyBag, createOptionsObserver({ largestUnit: "hours" }));
|
||||
|
@ -20,5 +20,3 @@ const earlier = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
|
||||
const later = new Temporal.ZonedDateTime(1_000_000_000_000_000_005n, "UTC");
|
||||
const result = earlier.until(later, { roundingIncrement: 2.5, roundingMode: "trunc" });
|
||||
TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
|
||||
const result2 = earlier.until(later, { smallestUnit: "days", roundingIncrement: 1e9 + 0.5, roundingMode: "expand" });
|
||||
TemporalHelpers.assertDuration(result2, 0, 0, 0, 1e9, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9");
|
||||
|
@ -324,7 +324,7 @@ var roundAndBalanceResults = {
|
||||
years: {
|
||||
years: "P6Y",
|
||||
months: "P5Y6M",
|
||||
weeks: "P5Y5M6W",
|
||||
weeks: "P5Y6M1W",
|
||||
days: "P5Y6M10D",
|
||||
hours: "P5Y6M10DT5H",
|
||||
minutes: "P5Y6M10DT5H5M",
|
||||
@ -335,7 +335,7 @@ var roundAndBalanceResults = {
|
||||
},
|
||||
months: {
|
||||
months: "P66M",
|
||||
weeks: "P65M6W",
|
||||
weeks: "P66M1W",
|
||||
days: "P66M10D",
|
||||
hours: "P66M10DT5H",
|
||||
minutes: "P66M10DT5H5M",
|
||||
|
@ -286,7 +286,7 @@ var totalDays = hours25.total({
|
||||
unit: "days",
|
||||
relativeTo
|
||||
});
|
||||
assert(Math.abs(totalDays - (1 + 1 / 24)) < Number.EPSILON);
|
||||
assert(Math.abs(totalDays - (1 + 1 / 23)) < Number.EPSILON);
|
||||
assert.sameValue(oneDay.total({
|
||||
unit: "hours",
|
||||
relativeTo
|
||||
|
Loading…
x
Reference in New Issue
Block a user