Temporal: Remove tests using relativeTo in Duration.p.add/subtract

See tc39/proposal-temporal#2825. This is a mass removal of tests that use
this functionality, in a separate commit for ease of review. Further
adjustments will be made in the following commit.
This commit is contained in:
Philip Chimento 2024-05-07 17:17:26 -07:00 committed by Philip Chimento
parent ba9c397923
commit ea4945c66f
139 changed files with 25 additions and 4736 deletions

View File

@ -10,20 +10,6 @@ features: [Temporal]
const instance = new Temporal.Duration();
const cases = [
// 2^32 = 4294967296
["P4294967296Y", "string with years > max"],
[{ years: 4294967296 }, "property bag with years > max"],
["-P4294967296Y", "string with years < min"],
[{ years: -4294967296 }, "property bag with years < min"],
["P4294967296M", "string with months > max"],
[{ months: 4294967296 }, "property bag with months > max"],
["-P4294967296M", "string with months < min"],
[{ months: -4294967296 }, "property bag with months < min"],
["P4294967296W", "string with weeks > max"],
[{ weeks: 4294967296 }, "property bag with weeks > max"],
["-P4294967296W", "string with weeks < min"],
[{ weeks: -4294967296 }, "property bag with weeks < min"],
// ceil(max safe integer / 86400) = 104249991375
["P104249991375D", "string with days > max"],
[{ days: 104249991375 }, "property bag with days > max"],

View File

@ -3,24 +3,7 @@
/*---
esid: sec-temporal.duration.prototype.add
description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
info: |
sec-temporal-nanosecondstodays step 6:
6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
sec-temporal-balanceduration step 4:
4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
sec-temporal-addduration steps 56:
5. If _relativeTo_ is *undefined*, then
...
b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
...
6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
...
n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
sec-temporal.duration.prototype.add step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
description: A negative duration result is balanced only up to hours
includes: [temporalHelpers.js]
features: [Temporal]
---*/
@ -30,7 +13,3 @@ const duration2 = new Temporal.Duration(0, 0, 0, -1);
const resultNotRelative = duration1.add(duration2);
TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
const resultRelative = duration1.add(duration2, { relativeTo });
TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0);

View File

@ -3,58 +3,27 @@
/*---
esid: sec-temporal.duration.prototype.add
description: Negative time fields in relativeTo are balanced upwards
info: |
sec-temporal-balancetime steps 314:
3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
4. Set _nanosecond_ to _nanosecond_ modulo 1000.
5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
6. Set _microsecond_ to _microsecond_ modulo 1000.
7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
8. Set _millisecond_ to _millisecond_ modulo 1000.
9. Set _minute_ to _minute_ + floor(_second_ / 60).
10. Set _second_ to _second_ modulo 60.
11. Set _hour_ to _hour_ + floor(_minute_ / 60).
12. Set _minute_ to _minute_ modulo 60.
13. Let _days_ be floor(_hour_ / 24).
14. Set _hour_ to _hour_ modulo 24.
sec-temporal-differencetime step 8:
8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
sec-temporal-differenceisodatetime step 2:
2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
sec-temporal-differencezoneddatetime step 7:
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_).
sec-temporal-addduration step 7.g.i:
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
sec-temporal.duration.prototype.add step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
description: Negative time fields are balanced upwards
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
const timeZone = new Temporal.TimeZone("UTC");
const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
// This code path is encountered if largestUnit is years, months, weeks, or days
// and relativeTo is a ZonedDateTime
const options = { largestUnit: "days", relativeTo };
const result1 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2), options);
const result1 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
const result2 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2), options);
const result2 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
const result3 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2), options);
const result3 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
const result4 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2), options);
const result4 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
const result5 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, -2), options);
const result5 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
// This one is different because hours are later balanced again in BalanceDuration
const result6 = duration.add(new Temporal.Duration(0, 0, 0, 0, -2), options);
const result6 = duration.add(new Temporal.Duration(0, 0, 0, 0, -2));
TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");

View File

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

View File

@ -1,18 +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.duration.prototype.add
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.Duration(1, 1, 1, 1);
const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
calendar.specificPlainDate = relativeTo;
instance.add(instance, { relativeTo });
assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");

View File

@ -1,40 +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.add
description: Duration.prototype.add should call dateAdd with the appropriate values.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
let calls = 0;
const expected = [
{
plainDate: [1920, 5, "M05", 3],
duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0],
},
{
plainDate: [1922, 5, "M05", 7],
duration: [0, 10, 0, 0, 0, 0, 0, 0, 0, 0],
},
];
class CustomCalendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
dateAdd(plainDate, duration, options) {
TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate,
`plainDate argument ${calls}`);
TemporalHelpers.assertDuration(duration, ...expected[calls].duration,
`duration argument ${calls}`);
assert.sameValue(options, undefined, "options argument");
++calls;
return super.dateAdd(plainDate, duration, options);
}
}
const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar());
const duration = new Temporal.Duration(2, 0, 0, 4, 2);
const result = duration.add({ months: 10, hours: 14 }, { relativeTo });
TemporalHelpers.assertDuration(result, 2, 10, 0, 4, 16, 0, 0, 0, 0, 0, "result");
assert.sameValue(calls, 2, "should have called dateAdd");

View File

@ -1,78 +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.duration.prototype.add
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
info: |
sec-temporal.duration.prototype.add step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
sec-temporal-addduration steps 6-7:
6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
...
j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
...
7. Else,
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-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 911:
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
(calendar, largestUnit, index) => {
const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
one.add(two, { relativeTo, largestUnit });
},
{
years: ["year"],
months: ["month"],
weeks: ["week"],
days: [],
hours: [],
minutes: [],
seconds: [],
milliseconds: [],
microseconds: [],
nanoseconds: []
}
);
TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
(calendar, largestUnit, index) => {
const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
one.add(two, { relativeTo, largestUnit });
},
{
years: ["year"],
months: ["month"],
weeks: ["week"],
days: [],
hours: [],
minutes: [],
seconds: [],
milliseconds: [],
microseconds: [],
nanoseconds: []
}
);

View File

@ -1,32 +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.duration.prototype.add
description: Verify the result of calendar.fields() is treated correctly.
info: |
sec-temporal.duration.prototype.add step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.c:
c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
sec-temporal-calendarfields step 4:
4. Let _result_ be ? IterableToList(_fieldsArray_).
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"day",
"month",
"monthCode",
"year",
];
const calendar = TemporalHelpers.calendarFieldsIterable();
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");

View File

@ -1,27 +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.duration.prototype.add
description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
info: |
sec-temporal.duration.prototype.add step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.b:
b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
sec-temporal-gettemporalcalendarwithisodefault step 2:
2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
sec-temporal-totemporalcalendarwithisodefault step 2b
3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
sec-temporal-totemporalcalendar step 1.a:
a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
i. Return _temporalCalendarLike_.[[Calendar]].
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
});

View File

@ -1,14 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));

View File

@ -1,52 +0,0 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
Throws a RangeError when custom calendar method returns inconsistent result
info: |
DifferenceZonedDateTime ( ... )
8. Repeat 3 times:
...
g. If _sign_ = 0, or _timeSign_ = 0, or _sign_ = _timeSign_, then
...
viii. Return ? CreateNormalizedDurationRecord(_dateDifference_.[[Years]],
_dateDifference_.[[Months]], _dateDifference_.[[Weeks]],
_dateDifference_.[[Days]], _norm_).
h. Set _dayCorrection_ to _dayCorrection_ + 1.
9. NOTE: This step is only reached when custom calendar or time zone methods
return inconsistent values.
10. Throw a *RangeError* exception.
features: [Temporal]
---*/
// Based partly on a test case by André Bargull
const duration1 = new Temporal.Duration(0, 0, /* weeks = */ 7, 0, /* hours = */ 12);
const duration2 = new Temporal.Duration(0, 0, 0, /* days = */ 1);
{
const tz = new (class extends Temporal.TimeZone {
getPossibleInstantsFor(dateTime) {
return super.getPossibleInstantsFor(dateTime.add({ days: 3 }));
}
})("UTC");
const relativeTo = new Temporal.ZonedDateTime(0n, tz);
assert.throws(RangeError, () => duration1.add(duration2, { relativeTo }),
"Calendar calculation where more than 2 days correction is needed should cause RangeError");
}
{
const cal = new (class extends Temporal.Calendar {
dateUntil(one, two, options) {
return super.dateUntil(one, two, options).negated();
}
})("iso8601");
const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", cal);
assert.throws(RangeError, () => duration1.add(duration2, { relativeTo }),
"Calendar calculation causing mixed-sign values should cause RangeError");
}

View File

@ -1,16 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));
}

View File

@ -10,10 +10,9 @@ features: [Temporal]
const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
fields.forEach((field) => {
assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { relativeTo }));
assert.throws(RangeError, () => instance.add({ [field]: Infinity }));
});
let calls = 0;
@ -26,6 +25,6 @@ const obj = {
fields.forEach((field) => {
calls = 0;
assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
assert.throws(RangeError, () => instance.add({ [field]: obj }));
assert.sameValue(calls, 1, "it fails after fetching the primitive value");
});

View File

@ -1,18 +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.add
description: >
AddZonedDateTime throws a RangeError when the intermediate instant is too large.
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
var duration = Temporal.Duration.from({days: 1, seconds: Number.MAX_SAFE_INTEGER - 86400});
var options = {relativeTo: zonedDateTime};
assert.throws(RangeError, () => duration.add(duration, options));

View File

@ -9,9 +9,6 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
// Largest temporal unit is "day".
const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER});
const duration2 = Temporal.Duration.from({nanoseconds: 2, days: 1});
@ -29,29 +26,3 @@ TemporalHelpers.assertDuration(
Number(nanos % 1000n),
"duration1.add(duration2)"
);
TemporalHelpers.assertDuration(
duration1.add(duration2, {relativeTo: plainDate}),
0, 0, 0,
1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.add(duration2, {relativeTo: plainDate})"
);
TemporalHelpers.assertDuration(
duration1.add(duration2, {relativeTo: zonedDateTime}),
0, 0, 0,
1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.add(duration2, {relativeTo: zonedDateTime})"
);

View File

@ -10,10 +10,9 @@ features: [Temporal]
const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
fields.forEach((field) => {
assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { relativeTo }));
assert.throws(RangeError, () => instance.add({ [field]: -Infinity }));
});
let calls = 0;
@ -26,6 +25,6 @@ const obj = {
fields.forEach((field) => {
calls = 0;
assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo }));
assert.throws(RangeError, () => instance.add({ [field]: obj }));
assert.sameValue(calls, 1, "it fails after fetching the primitive value");
});

View File

@ -1,23 +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.add
description: Empty or a function object may be used as options
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 0, 1);
const result1 = instance.add({ hours: 1 }, {});
TemporalHelpers.assertDuration(
result1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
"options may be an empty plain object"
);
const result2 = instance.add({ hours: 1 }, () => {});
TemporalHelpers.assertDuration(
result2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
"options may be a function object"
);

View File

@ -1,32 +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.duration.prototype.add
description: Verify that undefined options are handled correctly.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
const duration3 = new Temporal.Duration(0, 0, 0, 1);
const duration4 = new Temporal.Duration(0, 0, 0, 0, 24);
assert.throws(RangeError, () => duration1.add(duration2), "no options with years");
TemporalHelpers.assertDuration(duration3.add(duration4),
0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0,
"no options with days");
const optionValues = [
[undefined, "undefined"],
[{}, "plain object"],
[() => {}, "lambda"],
];
for (const [options, description] of optionValues) {
assert.throws(RangeError, () => duration1.add(duration2, options),
`options ${description} with years`);
TemporalHelpers.assertDuration(duration3.add(duration4, options),
0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0,
`options ${description} with days`);
}

View File

@ -1,23 +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.add
description: TypeError thrown when options argument is a primitive
features: [BigInt, Symbol, Temporal]
---*/
const badOptions = [
null,
true,
"some string",
Symbol(),
1,
2n,
];
const instance = new Temporal.Duration(0, 0, 0, 0, 1);
for (const value of badOptions) {
assert.throws(TypeError, () => instance.add({ hours: 1 }, value),
`TypeError on wrong options type ${typeof value}`);
};

View File

@ -40,8 +40,6 @@ const expected = [
"get fields.years",
"get fields.years.valueOf",
"call fields.years.valueOf",
// ToRelativeTemporalObject
"get options.relativeTo",
];
const actual = [];
@ -58,423 +56,8 @@ const simpleFields = TemporalHelpers.propertyBagObserver(actual, {
nanoseconds: 1,
}, "fields");
function createOptionsObserver(relativeTo = undefined) {
return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options");
}
// basic order of observable operations, without any calendar units:
const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
simpleInstance.add(simpleFields, createOptionsObserver());
simpleInstance.add(simpleFields);
assert.compareArray(actual, expected, "order of operations");
actual.splice(0); // clear
const expectedOpsForPlainRelativeTo = expected.concat([
// ToRelativeTemporalObject
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.microsecond",
"get options.relativeTo.millisecond",
"get options.relativeTo.minute",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.offset",
"get options.relativeTo.second",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
// AddDuration
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateUntil",
]);
const instance = new Temporal.Duration(1, 2, 0, 4, 5, 6, 7, 987, 654, 321);
const fields = TemporalHelpers.propertyBagObserver(actual, {
years: 1,
months: 1,
weeks: 1,
days: 1,
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
year: 2000,
month: 1,
monthCode: "M01",
day: 1,
calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
}, "options.relativeTo");
instance.add(fields, createOptionsObserver(plainRelativeTo));
assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
actual.splice(0); // clear
const expectedOpsForPlainRelativeToNoCalendarOperations = [
// ToTemporalDurationRecord
"get fields.days",
"get fields.days.valueOf",
"call fields.days.valueOf",
"get fields.hours",
"get fields.hours.valueOf",
"call fields.hours.valueOf",
"get fields.microseconds",
"get fields.microseconds.valueOf",
"call fields.microseconds.valueOf",
"get fields.milliseconds",
"get fields.milliseconds.valueOf",
"call fields.milliseconds.valueOf",
"get fields.minutes",
"get fields.minutes.valueOf",
"call fields.minutes.valueOf",
"get fields.months",
"get fields.nanoseconds",
"get fields.nanoseconds.valueOf",
"call fields.nanoseconds.valueOf",
"get fields.seconds",
"get fields.seconds.valueOf",
"call fields.seconds.valueOf",
"get fields.weeks",
"get fields.years",
// ToRelativeTemporalObject
"get options.relativeTo",
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.microsecond",
"get options.relativeTo.millisecond",
"get options.relativeTo.minute",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.offset",
"get options.relativeTo.second",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
];
const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
days: 1,
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
noCalendarInstance.add(noCalendarFields, createOptionsObserver(plainRelativeTo));
assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
actual.splice(0); // clear
const expectedOpsForZonedRelativeTo = expected.concat([
// ToRelativeTemporalObject
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.hour.valueOf",
"call options.relativeTo.hour.valueOf",
"get options.relativeTo.microsecond",
"get options.relativeTo.microsecond.valueOf",
"call options.relativeTo.microsecond.valueOf",
"get options.relativeTo.millisecond",
"get options.relativeTo.millisecond.valueOf",
"call options.relativeTo.millisecond.valueOf",
"get options.relativeTo.minute",
"get options.relativeTo.minute.valueOf",
"call options.relativeTo.minute.valueOf",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.nanosecond.valueOf",
"call options.relativeTo.nanosecond.valueOf",
"get options.relativeTo.offset",
"get options.relativeTo.offset.toString",
"call options.relativeTo.offset.toString",
"get options.relativeTo.second",
"get options.relativeTo.second.valueOf",
"call options.relativeTo.second.valueOf",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// ToRelativeTemporalObject again
"has options.relativeTo.timeZone.getOffsetNanosecondsFor",
"has options.relativeTo.timeZone.getPossibleInstantsFor",
"has options.relativeTo.timeZone.id",
// InterpretISODateTimeOffset
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
// AddDuration
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// AddDuration → AddZonedDateTime 1
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → AddZonedDateTime 2
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → DifferenceZonedDateTime
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.calendar.dateUntil",
]);
const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
year: 2000,
month: 1,
monthCode: "M01",
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
}, "options.relativeTo");
instance.add(fields, createOptionsObserver(zonedRelativeTo));
assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo");
actual.splice(0); // clear
const expectedOpsForZonedRelativeToNoDaysOperations = [
// ToTemporalDurationRecord
"get fields.days",
"get fields.hours",
"get fields.hours.valueOf",
"call fields.hours.valueOf",
"get fields.microseconds",
"get fields.microseconds.valueOf",
"call fields.microseconds.valueOf",
"get fields.milliseconds",
"get fields.milliseconds.valueOf",
"call fields.milliseconds.valueOf",
"get fields.minutes",
"get fields.minutes.valueOf",
"call fields.minutes.valueOf",
"get fields.months",
"get fields.nanoseconds",
"get fields.nanoseconds.valueOf",
"call fields.nanoseconds.valueOf",
"get fields.seconds",
"get fields.seconds.valueOf",
"call fields.seconds.valueOf",
"get fields.weeks",
"get fields.years",
// ToRelativeTemporalObject
"get options.relativeTo",
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.hour.valueOf",
"call options.relativeTo.hour.valueOf",
"get options.relativeTo.microsecond",
"get options.relativeTo.microsecond.valueOf",
"call options.relativeTo.microsecond.valueOf",
"get options.relativeTo.millisecond",
"get options.relativeTo.millisecond.valueOf",
"call options.relativeTo.millisecond.valueOf",
"get options.relativeTo.minute",
"get options.relativeTo.minute.valueOf",
"call options.relativeTo.minute.valueOf",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.nanosecond.valueOf",
"call options.relativeTo.nanosecond.valueOf",
"get options.relativeTo.offset",
"get options.relativeTo.offset.toString",
"call options.relativeTo.offset.toString",
"get options.relativeTo.second",
"get options.relativeTo.second.valueOf",
"call options.relativeTo.second.valueOf",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// ToRelativeTemporalObject again
"has options.relativeTo.timeZone.getOffsetNanosecondsFor",
"has options.relativeTo.timeZone.getPossibleInstantsFor",
"has options.relativeTo.timeZone.id",
// InterpretISODateTimeOffset
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
];
const noDaysInstance = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7, 987, 654, 321);
const noDaysFields = TemporalHelpers.propertyBagObserver(actual, {
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
noDaysInstance.add(noDaysFields, createOptionsObserver(zonedRelativeTo));
assert.compareArray(actual, expectedOpsForZonedRelativeToNoDaysOperations, "order of operations with ZonedDateTime relativeTo and no units above days");
actual.splice(0); // clear

View File

@ -9,9 +9,6 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
// Largest temporal unit is "day".
const duration1 = Temporal.Duration.from({seconds: 4503599627370495, nanoseconds: 499_999_999});
const duration2 = Temporal.Duration.from({seconds: 4503599627370495 - 86400, nanoseconds: 499_999_999, days: 1});
@ -29,21 +26,3 @@ TemporalHelpers.assertDuration(
Number(nanos % 1000n),
"duration1.add(duration2)"
);
TemporalHelpers.assertDuration(
duration1.add(duration2, {relativeTo: plainDate}),
0, 0, 0,
Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.add(duration2, {relativeTo: plainDate})"
);
// Throws a RangeError because the intermediate instant is too large.
assert.throws(RangeError, () => {
duration1.add(duration2, {relativeTo: zonedDateTime});
});

View File

@ -1,14 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }));

View File

@ -1,22 +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.duration.prototype.add
description: The time fields are read from the object before being passed to dateFromFields().
info: |
sec-temporal.duration.prototype.add step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.g:
g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
sec-temporal-interprettemporaldatetimefields steps 12:
1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });

View File

@ -1,23 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Throws if any value in the property bag is Infinity or -Infinity
esid: sec-temporal.duration.prototype.add
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
[Infinity, -Infinity].forEach((inf) => {
["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
const calls = [];
const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: obj } }));
assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
});
});

View File

@ -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.add
description: Leap second is constrained in both an ISO string and a property bag
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = "2016-12-31T23:59:60";
const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(
result1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"leap second is a valid ISO string for PlainDate relativeTo"
);
relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(
result2,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"leap second is a valid ISO string for ZonedDateTime relativeTo"
);
relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(
result3,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"second: 60 is valid in a property bag for PlainDate relativeTo"
);
relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(
result4,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"second: 60 is valid in a property bag for ZonedDateTime relativeTo"
);

View File

@ -1,18 +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.add
description: relativeTo with months.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const oneMonth = new Temporal.Duration(0, 1);
const days30 = new Temporal.Duration(0, 0, 0, 30);
TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-01-01') }),
0, 2, 0, 2, 0, 0, 0, 0, 0, 0, "January");
TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }),
0, 1, 0, 30, 0, 0, 0, 0, 0, 0, "February");
TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }),
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "March");

View File

@ -1,25 +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.add
description: A number cannot be used in place of a relativeTo
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
const numbers = [
1,
-20191101,
20191101,
1234567890,
];
for (const relativeTo of numbers) {
assert.throws(
TypeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
`A number (${relativeTo}) is not a valid ISO string for relativeTo`
);
}

View File

@ -1,16 +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.add
description: relativeTo with years.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d1 = new Temporal.Duration(0, 1, 0, 0);
const d2 = new Temporal.Duration(0, 0, 0, 30);
const relativeTo = new Temporal.PlainDate(2000, 1, 1);
TemporalHelpers.assertDuration(d1.add(d2, { relativeTo }),
0, 2, 0, 1, 0, 0, 0, 0, 0, 0,
"first this is resolved against relativeTo, then the argument against relativeTo + this");

View File

@ -1,89 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
Correct time zone calls are made when converting a ZonedDateTime-like
relativeTo property bag denoting an ambiguous wall-clock time
includes: [temporalHelpers.js, compareArray.js]
features: [Temporal]
---*/
const actual = [];
const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
});
const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
const expected = [
// GetTemporalCalendarSlotValueWithISODefault
"has calendar.dateAdd",
"has calendar.dateFromFields",
"has calendar.dateUntil",
"has calendar.day",
"has calendar.dayOfWeek",
"has calendar.dayOfYear",
"has calendar.daysInMonth",
"has calendar.daysInWeek",
"has calendar.daysInYear",
"has calendar.fields",
"has calendar.id",
"has calendar.inLeapYear",
"has calendar.mergeFields",
"has calendar.month",
"has calendar.monthCode",
"has calendar.monthDayFromFields",
"has calendar.monthsInYear",
"has calendar.weekOfYear",
"has calendar.year",
"has calendar.yearMonthFromFields",
"has calendar.yearOfWeek",
// lookup
"get calendar.dateFromFields",
"get calendar.fields",
// CalendarFields
"call calendar.fields",
// InterpretTemporalDateTimeFields
"call calendar.dateFromFields",
// ToTemporalTimeZoneSlotValue
"has timeZone.getOffsetNanosecondsFor",
"has timeZone.getPossibleInstantsFor",
"has timeZone.id",
// lookup
"get timeZone.getOffsetNanosecondsFor",
"get timeZone.getPossibleInstantsFor",
// InterpretISODateTimeOffset
"call timeZone.getPossibleInstantsFor",
];
const expectedSpringForward = expected.concat([
// DisambiguatePossibleInstants
"call timeZone.getOffsetNanosecondsFor",
"call timeZone.getOffsetNanosecondsFor",
"call timeZone.getPossibleInstantsFor",
]);
assert.compareArray(
actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
expectedSpringForward,
"order of operations converting property bag at skipped wall-clock time"
);
actual.splice(0); // clear
relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
assert.compareArray(
actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
expected,
"order of operations converting property bag at repeated wall-clock time"
);
actual.splice(0); // clear

View File

@ -1,22 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
Calling the method with a relativeTo property bag with a builtin calendar
causes no observable array iteration when getting the calendar fields.
features: [Temporal]
---*/
const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
Array.prototype[Symbol.iterator] = function arrayIterator() {
throw new Test262Error("Array should not be iterated");
}
const timeZone = "UTC";
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;

View File

@ -1,16 +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.add
description: >
Calendar.dateFromFields method is called with a null-prototype fields object
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, calendar };
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");

View File

@ -1,26 +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.add
description: A number as calendar in relativeTo property bag is invalid
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
const numbers = [
1,
19970327,
-19970327,
1234567890,
];
for (const calendar of numbers) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(
TypeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
`A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
);
}

View File

@ -1,26 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
Builtin dateFromFields method is not observably called when the property bag
has a string-valued calendar property
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
configurable: true,
enumerable: false,
get() {
TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
},
});
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);

View File

@ -1,45 +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.add
description: >
Appropriate error thrown when relativeTo.calendar cannot be converted to a
calendar object or string
features: [BigInt, Symbol, Temporal]
---*/
const timeZone = new Temporal.TimeZone("UTC");
const instance = new Temporal.Duration(1, 0, 0, 1);
const primitiveTests = [
[null, "null"],
[true, "boolean"],
["", "empty string"],
[1, "number that doesn't convert to a valid ISO string"],
[1n, "bigint"],
];
for (const [calendar, description] of primitiveTests) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(
typeof calendar === 'string' ? RangeError : TypeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
`${description} does not convert to a valid ISO string`
);
}
const typeErrorTests = [
[Symbol(), "symbol"],
[{}, "plain object that doesn't implement the protocol"],
[new Temporal.TimeZone("UTC"), "time zone instance"],
[Temporal.PlainDate, "Temporal.PlainDate, object"],
[Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
[Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
[Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
];
for (const [calendar, description] of typeErrorTests) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
}

View File

@ -1,56 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
built-in ISO 8601 calendar
features: [Temporal]
info: |
DisambiguatePossibleInstants:
2. Let _n_ be _possibleInstants_'s length.
...
5. Assert: _n_ = 0.
...
19. If _disambiguation_ is *"earlier"*, then
...
c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
...
20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
...
23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
---*/
class SkippedDateTime extends Temporal.TimeZone {
constructor() {
super("UTC");
this.calls = 0;
}
getPossibleInstantsFor(dateTime) {
// Calls occur in pairs. For the first one return no possible instants so
// that DisambiguatePossibleInstants will call it again
if (this.calls++ % 2 == 0) {
return [];
}
assert.sameValue(
dateTime.getISOFields().calendar,
"iso8601",
"getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
);
return super.getPossibleInstantsFor(dateTime);
}
}
const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
const timeZone = new SkippedDateTime();
const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
const instance = new Temporal.Duration(1, 0, 0, 1);
instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");

View File

@ -1,29 +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.add
description: relativeTo property bag with offset property is rejected if offset is in the wrong format
features: [Temporal]
---*/
const timeZone = new Temporal.TimeZone("UTC");
const instance = new Temporal.Duration(1, 0, 0, 1);
const badOffsets = [
"00:00", // missing sign
"+0", // too short
"-000:00", // too long
0, // must be a string
null, // must be a string
true, // must be a string
1000n, // must be a string
];
badOffsets.forEach((offset) => {
const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone };
assert.throws(
typeof(offset) === 'string' ? RangeError : TypeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
`"${offset} is not a valid offset string`
);
});

View File

@ -1,15 +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.duration.prototype.add
description: Missing time units in relativeTo property bag default to 0
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = { year: 2000, month: 1, day: 1 };
const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "missing time units default to 0");

View File

@ -1,47 +0,0 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
UTC offset shift returned by getPossibleInstantsFor can be at most 24 hours.
features: [Temporal]
info: |
GetPossibleInstantsFor:
5.b.i. Let _numResults_ be _list_'s length.
ii. If _numResults_ > 1, then
1. Let _epochNs_ be a new empty List.
2. For each value _instant_ in list, do
a. Append _instant_.[[EpochNanoseconds]] to the end of the List _epochNs_.
3. Let _min_ be the least element of the List _epochNs_.
4. Let _max_ be the greatest element of the List _epochNs_.
5. If abs((_max_ - _min_)) > nsPerDay, throw a *RangeError* exception.
---*/
class ShiftLonger24Hour extends Temporal.TimeZone {
id = 'TestTimeZone';
constructor() {
super('UTC');
}
getOffsetNanosecondsFor(instant) {
return 0;
}
getPossibleInstantsFor(plainDateTime) {
const utc = new Temporal.TimeZone("UTC");
const [utcInstant] = utc.getPossibleInstantsFor(plainDateTime);
return [
utcInstant.subtract({ hours: 12, nanoseconds: 1 }),
utcInstant.add({ hours: 12 }),
utcInstant, // add a third value in case the implementation doesn't sort
];
}
}
const timeZone = new ShiftLonger24Hour();
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "RangeError should be thrown");

View File

@ -1,42 +0,0 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: >
UTC offset shift returned by adjacent invocations of getOffsetNanosecondsFor
in DisambiguatePossibleInstants cannot be greater than 24 hours.
features: [Temporal]
info: |
DisambiguatePossibleInstants:
18. If abs(_nanoseconds_) > nsPerDay, throw a *RangeError* exception.
---*/
class ShiftLonger24Hour extends Temporal.TimeZone {
id = 'TestTimeZone';
_shiftEpochNs = 12n * 3600n * 1_000_000_000n; // 1970-01-01T12:00Z
constructor() {
super('UTC');
}
getOffsetNanosecondsFor(instant) {
if (instant.epochNanoseconds < this._shiftEpochNs) return -12 * 3600e9;
return 12 * 3600e9 + 1;
}
getPossibleInstantsFor(plainDateTime) {
const [utcInstant] = super.getPossibleInstantsFor(plainDateTime);
const { year, month, day } = plainDateTime;
if (year < 1970) return [utcInstant.subtract({ hours: 12 })];
if (year === 1970 && month === 1 && day === 1) return [];
return [utcInstant.add({ hours: 12, nanoseconds: 1 })];
}
}
const timeZone = new ShiftLonger24Hour();
const relativeTo = { year: 1970, month: 1, day: 1, hour: 12, timeZone };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "RangeError should be thrown");

View File

@ -1,16 +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.duration.prototype.add
description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
});

View File

@ -1,20 +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.duration.prototype.add
description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
features: [BigInt, Symbol, Temporal, arrow-function]
---*/
[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
const timeZone = new Temporal.TimeZone("UTC");
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
timeZone.getOffsetNanosecondsFor = notCallable;
assert.throws(
TypeError,
() => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }),
`Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
);
});

View File

@ -1,16 +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.duration.prototype.add
description: RangeError thrown if time zone reports an offset that is out of range
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
});

View File

@ -1,25 +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.duration.prototype.add
description: TypeError thrown if time zone reports an offset that is not a Number
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[
undefined,
null,
true,
"+01:00",
Symbol(),
3600_000_000_000n,
{},
{ valueOf() { return 3600_000_000_000; } },
].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
assert.throws(TypeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }));
});

View File

@ -1,63 +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.duration.prototype.add
description: Conversion of ISO date-time strings to Temporal.TimeZone instances
features: [Temporal]
---*/
const instance = new Temporal.Duration(1);
let timeZone = "2021-08-19T17:30";
assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone");
[
"2021-08-19T17:30-07:00:01",
"2021-08-19T17:30-07:00:00",
"2021-08-19T17:30-07:00:00.1",
"2021-08-19T17:30-07:00:00.0",
"2021-08-19T17:30-07:00:00.01",
"2021-08-19T17:30-07:00:00.00",
"2021-08-19T17:30-07:00:00.001",
"2021-08-19T17:30-07:00:00.000",
"2021-08-19T17:30-07:00:00.0001",
"2021-08-19T17:30-07:00:00.0000",
"2021-08-19T17:30-07:00:00.00001",
"2021-08-19T17:30-07:00:00.00000",
"2021-08-19T17:30-07:00:00.000001",
"2021-08-19T17:30-07:00:00.000000",
"2021-08-19T17:30-07:00:00.0000001",
"2021-08-19T17:30-07:00:00.0000000",
"2021-08-19T17:30-07:00:00.00000001",
"2021-08-19T17:30-07:00:00.00000000",
"2021-08-19T17:30-07:00:00.000000001",
"2021-08-19T17:30-07:00:00.000000000",
].forEach((timeZone) => {
assert.throws(
RangeError,
() => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
`ISO string ${timeZone} with a sub-minute offset is not a valid time zone`
);
});
// The following are all valid strings so should not throw:
[
"2021-08-19T17:30Z",
"2021-08-19T1730Z",
"2021-08-19T17:30-07:00",
"2021-08-19T1730-07:00",
"2021-08-19T17:30-0700",
"2021-08-19T1730-0700",
"2021-08-19T17:30[UTC]",
"2021-08-19T1730[UTC]",
"2021-08-19T17:30Z[UTC]",
"2021-08-19T1730Z[UTC]",
"2021-08-19T17:30-07:00[UTC]",
"2021-08-19T1730-07:00[UTC]",
"2021-08-19T17:30-0700[UTC]",
"2021-08-19T1730-0700[UTC]",
].forEach((timeZone) => {
instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
});

View File

@ -1,19 +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.add
description: Leap second is a valid ISO string for TimeZone
features: [Temporal]
---*/
const instance = new Temporal.Duration(1);
let timeZone = "2016-12-31T23:59:60+00:00[UTC]";
// A string with a leap second is a valid ISO string, so the following
// operation should not throw
instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]";
assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid");

View File

@ -1,21 +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.add
description: Negative zero, as an extended year, is rejected
features: [Temporal, arrow-function]
---*/
const invalidStrings = [
"-000000-10-31T17:45Z",
"-000000-10-31T17:45+00:00[UTC]",
];
const instance = new Temporal.Duration(1);
invalidStrings.forEach((timeZone) => {
assert.throws(
RangeError,
() => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
"reject minus zero as extended year"
);
});

View File

@ -1,37 +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.add
description: Time zone IDs are valid input for a time zone
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const getPossibleInstantsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getPossibleInstantsFor");
Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", {
configurable: true,
enumerable: false,
get() {
TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be looked up");
},
});
const getOffsetNanosecondsForOriginal = Object.getOwnPropertyDescriptor(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor");
Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", {
configurable: true,
enumerable: false,
get() {
TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be looked up");
},
});
const instance = new Temporal.Duration(1);
// The following are all valid strings so should not throw:
["UTC", "+01:00"].forEach((timeZone) => {
instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } });
});
Object.defineProperty(Temporal.TimeZone.prototype, "getPossibleInstantsFor", getPossibleInstantsForOriginal);
Object.defineProperty(Temporal.TimeZone.prototype, "getOffsetNanosecondsFor", getOffsetNanosecondsForOriginal);

View File

@ -1,39 +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.add
description: >
Appropriate error thrown when argument cannot be converted to a valid string
or object for TimeZone
features: [BigInt, Symbol, Temporal]
---*/
const instance = new Temporal.Duration(1);
const primitiveTests = [
[null, "null"],
[true, "boolean"],
["", "empty string"],
[1, "number that doesn't convert to a valid ISO string"],
[19761118, "number that would convert to a valid ISO string in other contexts"],
[1n, "bigint"],
];
for (const [timeZone, description] of primitiveTests) {
assert.throws(
typeof timeZone === 'string' ? RangeError : TypeError,
() => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }),
`${description} does not convert to a valid ISO string`
);
}
const typeErrorTests = [
[Symbol(), "symbol"],
[{}, "object not implementing time zone protocol"],
[new Temporal.Calendar("iso8601"), "calendar instance"],
];
for (const [timeZone, description] of typeErrorTests) {
assert.throws(TypeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`);
}

View File

@ -1,33 +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.add
description: relativeTo is required if the largest unit is at least weeks.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d = Temporal.Duration.from({ hours: 1 });
const dy = Temporal.Duration.from({ years: 1 });
const dm = Temporal.Duration.from({ months: 1 });
const dw = Temporal.Duration.from({ weeks: 1 });
assert.throws(RangeError, () => d.add(dy));
assert.throws(RangeError, () => d.add(dm));
assert.throws(RangeError, () => d.add(dw));
assert.throws(RangeError, () => dy.add(d));
assert.throws(RangeError, () => dm.add(d));
assert.throws(RangeError, () => dw.add(d));
const relativeTo = Temporal.PlainDate.from("2000-01-01");
TemporalHelpers.assertDuration(d.add(dy, { relativeTo }),
1, 0, 0, 0, 1, 0, 0, 0, 0, 0);
TemporalHelpers.assertDuration(d.add(dm, { relativeTo }),
0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
TemporalHelpers.assertDuration(d.add(dw, { relativeTo }),
0, 0, 1, 0, 1, 0, 0, 0, 0, 0);
TemporalHelpers.assertDuration(dy.add(d, { relativeTo }),
1, 0, 0, 0, 1, 0, 0, 0, 0, 0);
TemporalHelpers.assertDuration(dm.add(d, { relativeTo }),
0, 1, 0, 0, 1, 0, 0, 0, 0, 0);
TemporalHelpers.assertDuration(dw.add(d, { relativeTo }),
0, 0, 1, 0, 1, 0, 0, 0, 0, 0);

View File

@ -1,38 +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.duration.prototype.add
description: >
Conversion of ISO date-time strings as relativeTo option to
Temporal.ZonedDateTime or Temporal.PlainDateTime instances
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = "2019-11-01T00:00";
const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo");
relativeTo = "2019-11-01T00:00-07:00";
const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo");
relativeTo = "2019-11-01T00:00[-07:00]";
const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo");
relativeTo = "2019-11-01T00:00Z[-07:00]";
const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo");
relativeTo = "2019-11-01T00:00+00:00[UTC]";
const result5 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo");
relativeTo = "2019-11-01T00:00Z";
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + Z throws without an IANA annotation");
relativeTo = "2019-11-01T00:00+04:15[UTC]";
assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch");

View File

@ -1,13 +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.duration.prototype.add
description: RangeError thrown if relativeTo is a string with the wrong format
features: [Temporal]
---*/
['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => {
const duration = new Temporal.Duration(1, 0, 0, 15);
assert.throws(RangeError, () => duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo }));
});

View File

@ -1,15 +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.duration.prototype.add
description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string
features: [Temporal]
includes: [temporalHelpers.js]
---*/
['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => {
const duration = new Temporal.Duration(1, 0, 0, 15);
const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
});

View File

@ -1,16 +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.duration.prototype.add
description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = "2000-01-01T00:00+05:30[UTC]";
assert.throws(
RangeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
"add should throw RangeError on a string with UTC offset mismatch"
);

View File

@ -1,20 +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.duration.prototype.add
description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[
'2000-01-01[UTC]',
'2000-01-01T00:00[UTC]',
'2000-01-01T00:00+00:00[UTC]',
'2000-01-01T00:00+00:00[UTC][u-ca=iso8601]',
].forEach((relativeTo) => {
const duration = new Temporal.Duration(1, 0, 0, 15);
const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo });
TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0);
});

View File

@ -1,25 +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.duration.prototype.add
description: relativeTo string accepts trailing zeroes in sub-minute UTC offset
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let result;
let relativeTo;
const action = (relativeTo) => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo });
relativeTo = "1970-01-01T00:00-00:45:00[-00:45]";
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (string)");
relativeTo = { year: 1970, month: 1, day: 1, offset: "+00:45:00.000000000", timeZone: "+00:45" };
result = action(relativeTo);
TemporalHelpers.assertDateDuration(result, 1, 0, 0, 0, "ISO string offset accepted with zero seconds (property bag)");
relativeTo = "1970-01-01T00:00+00:44:30.123456789[+00:45]";
assert.throws(RangeError, () => action(relativeTo), "rounding is not accepted between ISO offset and time zone");

View File

@ -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.add
description: >
Appropriate error thrown when relativeTo cannot be converted to a valid
relativeTo string or property bag
features: [BigInt, Symbol, Temporal]
---*/
const timeZone = new Temporal.TimeZone("UTC");
const instance = new Temporal.Duration(1, 0, 0, 1);
const primitiveTests = [
[undefined, "undefined"],
[null, "null"],
[true, "boolean"],
["", "empty string"],
[1, "number that doesn't convert to a valid ISO string"],
[1n, "bigint"],
];
for (const [relativeTo, description] of primitiveTests) {
assert.throws(
typeof relativeTo === 'string' || typeof relativeTo === 'undefined' ? RangeError : TypeError,
() => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }),
`${description} does not convert to a valid ISO string (first argument)`
);
}
const typeErrorTests = [
[Symbol(), "symbol"],
[{}, "plain object"],
[Temporal.PlainDate, "Temporal.PlainDate, object"],
[Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
[Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
[Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
];
for (const [relativeTo, description] of typeErrorTests) {
assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
}

View File

@ -1,16 +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.add
description: relativeTo with years.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const oneYear = new Temporal.Duration(1);
const days365 = new Temporal.Duration(0, 0, 0, 365);
TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }),
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "non-leap year");
TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2015-01-01") }),
1, 11, 0, 30, 0, 0, 0, 0, 0, 0, "leap year");

View File

@ -1,24 +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.duration.prototype.add
description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
info: |
sec-temporal-getisopartsfromepoch step 1:
1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
sec-temporal-builtintimezonegetplaindatetimefor step 2:
2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
features: [Temporal]
includes: [temporalHelpers.js]
---*/
const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
const duration = new Temporal.Duration(0, 0, 0, 1);
// This code path shows up anywhere we convert an exact time, before the Unix
// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this
// case via relativeTo.
const result = duration.add(duration, { relativeTo });
TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);

View File

@ -1,17 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
assert.throws(RangeError, () => duration.add(other, { relativeTo }));
});

View File

@ -1,21 +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.duration.prototype.add
description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
features: [BigInt, Symbol, Temporal, arrow-function]
---*/
[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
const timeZone = new Temporal.TimeZone("UTC");
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
timeZone.getOffsetNanosecondsFor = notCallable;
assert.throws(
TypeError,
() => duration.add(other, { relativeTo }),
`Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
);
});

View File

@ -1,17 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: RangeError thrown if time zone reports an offset that is out of range
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
assert.throws(RangeError, () => duration.add(other, { relativeTo }));
});

View File

@ -1,26 +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.duration.prototype.add
description: TypeError thrown if time zone reports an offset that is not a Number
features: [Temporal]
includes: [temporalHelpers.js]
---*/
[
undefined,
null,
true,
"+01:00",
Symbol(),
3600_000_000_000n,
{},
{ valueOf() { return 3600_000_000_000; } },
].forEach((wrongOffset) => {
const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const other = new Temporal.Duration(2);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
assert.throws(TypeError, () => duration.add(other, { relativeTo }));
});

View File

@ -8,20 +8,9 @@ description: >
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
// Largest temporal unit is "second".
const duration = Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER});
assert.throws(RangeError, () => {
duration.add(duration);
});
assert.throws(RangeError, () => {
duration.add(duration, {relativeTo: plainDate});
});
assert.throws(RangeError, () => {
duration.add(duration, {relativeTo: zonedDateTime});
});

View File

@ -1,43 +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.duration.prototype.add
description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call
info: |
sec-temporal.duration.prototype.add steps 56:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], _other_.[[Years]], [...], _other_.[[Nanoseconds]], _relativeTo_).
sec-temporal-torelativetemporalobject step 6.d:
d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*).
sec-temporal-interpretisodatetimeoffset step 7:
7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
sec-temporal-addduration steps 7.de and 7.g.i:
d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_).
e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_).
[...]
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
sec-temporal-differencezoneddatetime step 8:
8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
sec-temporal-addzoneddatetime step 8:
8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*).
sec-temporal-builtintimezonegetinstantfor step 1:
1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_).
sec-temporal-getpossibleinstantsfor step 2:
2. Let _list_ be ? IterableToList(_possibleInstants_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"2000-01-01T09:00:00", // called once on the input relativeTo object
"2001-01-01T09:00:00", // called once on relativeTo plus the receiver
"2002-01-01T09:00:00", // called once on relativeTo plus the receiver plus the argument
"2002-01-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs
];
TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => {
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } });
}, expected);

View File

@ -1,17 +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.add
description: Negative zero, as an extended year, is rejected
features: [Temporal, arrow-function]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = "-000000-11-04T00:00";
assert.throws(
RangeError,
() => { instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); },
"reject minus zero as extended year"
);

View File

@ -10,20 +10,6 @@ features: [Temporal]
const instance = new Temporal.Duration();
const cases = [
// 2^32 = 4294967296
["P4294967296Y", "string with years > max"],
[{ years: 4294967296 }, "property bag with years > max"],
["-P4294967296Y", "string with years < min"],
[{ years: -4294967296 }, "property bag with years < min"],
["P4294967296M", "string with months > max"],
[{ months: 4294967296 }, "property bag with months > max"],
["-P4294967296M", "string with months < min"],
[{ months: -4294967296 }, "property bag with months < min"],
["P4294967296W", "string with weeks > max"],
[{ weeks: 4294967296 }, "property bag with weeks > max"],
["-P4294967296W", "string with weeks < min"],
[{ weeks: -4294967296 }, "property bag with weeks < min"],
// ceil(max safe integer / 86400) = 104249991375
["P104249991375D", "string with days > max"],
[{ days: 104249991375 }, "property bag with days > max"],

View File

@ -4,23 +4,6 @@
/*---
esid: sec-temporal.duration.prototype.subtract
description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays
info: |
sec-temporal-nanosecondstodays step 6:
6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }.
sec-temporal-balanceduration step 4:
4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then
a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_).
sec-temporal-addduration steps 56:
5. If _relativeTo_ is *undefined*, then
...
b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
...
6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
...
n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_).
sec-temporal.duration.prototype.subtract step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
@ -30,7 +13,3 @@ const duration2 = new Temporal.Duration(0, 0, 0, -1);
const resultNotRelative = duration1.subtract(duration2);
TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
const resultRelative = duration1.subtract(duration2, { relativeTo });
TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0);

View File

@ -3,58 +3,29 @@
/*---
esid: sec-temporal.duration.prototype.subtract
description: Negative time fields in relativeTo are balanced upwards
description: Negative time fields are balanced upwards
info: |
sec-temporal-balancetime steps 314:
3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
4. Set _nanosecond_ to _nanosecond_ modulo 1000.
5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
6. Set _microsecond_ to _microsecond_ modulo 1000.
7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
8. Set _millisecond_ to _millisecond_ modulo 1000.
9. Set _minute_ to _minute_ + floor(_second_ / 60).
10. Set _second_ to _second_ modulo 60.
11. Set _hour_ to _hour_ + floor(_minute_ / 60).
12. Set _minute_ to _minute_ modulo 60.
13. Let _days_ be floor(_hour_ / 24).
14. Set _hour_ to _hour_ modulo 24.
sec-temporal-differencetime step 8:
8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
sec-temporal-differenceisodatetime step 2:
2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_).
sec-temporal-differencezoneddatetime step 7:
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_).
sec-temporal-addduration step 7.g.i:
i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_).
sec-temporal.duration.prototype.subtract step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
const timeZone = new Temporal.TimeZone("UTC");
const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone);
// This code path is encountered if largestUnit is years, months, weeks, or days
// and relativeTo is a ZonedDateTime
const options = { largestUnit: "days", relativeTo };
const result1 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2), options);
const result1 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
const result2 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2), options);
const result2 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
const result3 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2), options);
const result3 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
const result4 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2), options);
const result4 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
const result5 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2), options);
const result5 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
// This one is different because hours are later balanced again in BalanceDuration
const result6 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 2), options);
const result6 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 2));
TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");

View File

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

View File

@ -1,18 +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.duration.prototype.subtract
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.Duration(1, 1, 1, 1);
const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar);
calendar.specificPlainDate = relativeTo;
instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo });
assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested");

View File

@ -1,40 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Duration.prototype.subtract should call dateAdd with the appropriate values.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
let calls = 0;
const expected = [
{
plainDate: [1920, 5, "M05", 3],
duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0],
},
{
plainDate: [1922, 5, "M05", 7],
duration: [0, -10, 0, 0, 0, 0, 0, 0, 0, 0],
},
];
class CustomCalendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
dateAdd(plainDate, duration, options) {
TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate,
`plainDate argument ${calls}`);
TemporalHelpers.assertDuration(duration, ...expected[calls].duration,
`duration argument ${calls}`);
assert.sameValue(options, undefined, "options argument");
++calls;
return super.dateAdd(plainDate, duration, options);
}
}
const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar());
const duration = new Temporal.Duration(2, 0, 0, 4, 2);
const result = duration.subtract({ months: 10, hours: 14 }, { relativeTo });
TemporalHelpers.assertDuration(result, 1, 2, 0, 3, 12, 0, 0, 0, 0, 0, "result");
assert.sameValue(calls, 2, "should have called dateAdd");

View File

@ -1,78 +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.duration.prototype.subtract
description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form
info: |
sec-temporal.duration.prototype.subtract step 6:
6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_).
sec-temporal-addduration steps 6-7:
6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then
...
j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*).
l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_).
m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_).
...
7. Else,
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-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 911:
9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_).
10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_).
11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_).
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
(calendar, largestUnit, index) => {
const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
two.subtract(one, { relativeTo });
},
{
years: ["year"],
months: ["month"],
weeks: ["week"],
days: [],
hours: [],
minutes: [],
seconds: [],
milliseconds: [],
microseconds: [],
nanoseconds: []
}
);
TemporalHelpers.checkCalendarDateUntilLargestUnitSingular(
(calendar, largestUnit, index) => {
const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]);
const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]);
const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar);
two.subtract(one, { relativeTo });
},
{
years: ["year"],
months: ["month"],
weeks: ["week"],
days: [],
hours: [],
minutes: [],
seconds: [],
milliseconds: [],
microseconds: [],
nanoseconds: []
}
);

View File

@ -1,32 +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.duration.prototype.subtract
description: Verify the result of calendar.fields() is treated correctly.
info: |
sec-temporal.duration.prototype.subtract step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.c:
c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »).
sec-temporal-calendarfields step 4:
4. Let _result_ be ? IterableToList(_fieldsArray_).
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const expected = [
"day",
"month",
"monthCode",
"year",
];
const calendar = TemporalHelpers.calendarFieldsIterable();
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });
assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once");
assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args");
assert(calendar.iteratorExhausted[0], "iterated through the whole iterable");

View File

@ -1,27 +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.duration.prototype.subtract
description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots
info: |
sec-temporal.duration.prototype.subtract step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.b:
b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_).
sec-temporal-gettemporalcalendarwithisodefault step 2:
2. Return ? ToTemporalCalendarWithISODefault(_calendar_).
sec-temporal-totemporalcalendarwithisodefault step 2:
3. Return ? ToTemporalCalendar(_temporalCalendarLike_).
sec-temporal-totemporalcalendar step 1.a:
a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
i. Return _temporalCalendarLike_.[[Calendar]].
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => {
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } });
});

View File

@ -1,14 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: If a calendar's fields() method returns a field named 'constructor', PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarWithExtraFields(['constructor']);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));

View File

@ -1,52 +0,0 @@
// Copyright (C) 2024 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Throws a RangeError when custom calendar method returns inconsistent result
info: |
DifferenceZonedDateTime ( ... )
8. Repeat 3 times:
...
g. If _sign_ = 0, or _timeSign_ = 0, or _sign_ = _timeSign_, then
...
viii. Return ? CreateNormalizedDurationRecord(_dateDifference_.[[Years]],
_dateDifference_.[[Months]], _dateDifference_.[[Weeks]],
_dateDifference_.[[Days]], _norm_).
h. Set _dayCorrection_ to _dayCorrection_ + 1.
9. NOTE: This step is only reached when custom calendar or time zone methods
return inconsistent values.
10. Throw a *RangeError* exception.
features: [Temporal]
---*/
// Based partly on a test case by André Bargull
const duration1 = new Temporal.Duration(0, 0, /* weeks = */ 7, 0, /* hours = */ 12);
const duration2 = new Temporal.Duration(0, 0, 0, /* days = */ -1);
{
const tz = new (class extends Temporal.TimeZone {
getPossibleInstantsFor(dateTime) {
return super.getPossibleInstantsFor(dateTime.add({ days: 3 }));
}
})("UTC");
const relativeTo = new Temporal.ZonedDateTime(0n, tz);
assert.throws(RangeError, () => duration1.subtract(duration2, { relativeTo }),
"Calendar calculation where more than 2 days correction is needed should cause RangeError");
}
{
const cal = new (class extends Temporal.Calendar {
dateUntil(one, two, options) {
return super.dateUntil(one, two, options).negated();
}
})("iso8601");
const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", cal);
assert.throws(RangeError, () => duration1.subtract(duration2, { relativeTo }),
"Calendar calculation causing mixed-sign values should cause RangeError");
}

View File

@ -1,16 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: If a calendar's fields() method returns duplicate field names, PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
for (const extra_fields of [['foo', 'foo'], ['day'], ['month'], ['monthCode'], ['year']]) {
const calendar = TemporalHelpers.calendarWithExtraFields(extra_fields);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));
}

View File

@ -10,10 +10,9 @@ features: [Temporal]
const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
fields.forEach((field) => {
assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { relativeTo }));
assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }));
});
let calls = 0;
@ -26,6 +25,6 @@ const obj = {
fields.forEach((field) => {
calls = 0;
assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
assert.sameValue(calls, 1, "it fails after fetching the primitive value");
});

View File

@ -1,19 +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.subtract
description: >
AddZonedDateTime throws a RangeError when the intermediate instant is too large.
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
var duration1 = Temporal.Duration.from({days: 1, seconds: Number.MAX_SAFE_INTEGER - 86400});
var duration2 = Temporal.Duration.from({days: -1, seconds: -Number.MAX_SAFE_INTEGER + 86400});
var options = {relativeTo: zonedDateTime};
assert.throws(RangeError, () => duration1.subtract(duration2, options));

View File

@ -9,9 +9,6 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
// Largest temporal unit is "day".
const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER});
const duration2 = Temporal.Duration.from({nanoseconds: -2, days: -1});
@ -29,29 +26,3 @@ TemporalHelpers.assertDuration(
Number(nanos % 1000n),
"duration1.subtract(duration2)"
);
TemporalHelpers.assertDuration(
duration1.subtract(duration2, {relativeTo: plainDate}),
0, 0, 0,
1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.subtract(duration2, {relativeTo: plainDate})"
);
TemporalHelpers.assertDuration(
duration1.subtract(duration2, {relativeTo: zonedDateTime}),
0, 0, 0,
1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.subtract(duration2, {relativeTo: zonedDateTime})"
);

View File

@ -10,10 +10,9 @@ features: [Temporal]
const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds'];
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const relativeTo = new Temporal.PlainDateTime(2000, 1, 1);
fields.forEach((field) => {
assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { relativeTo }));
assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }));
});
let calls = 0;
@ -26,6 +25,6 @@ const obj = {
fields.forEach((field) => {
calls = 0;
assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo }));
assert.throws(RangeError, () => instance.subtract({ [field]: obj }));
assert.sameValue(calls, 1, "it fails after fetching the primitive value");
});

View File

@ -1,23 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Empty or a function object may be used as options
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 0, 1);
const result1 = instance.subtract({ hours: 1 }, {});
TemporalHelpers.assertDuration(
result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"options may be an empty plain object"
);
const result2 = instance.subtract({ hours: 1 }, () => {});
TemporalHelpers.assertDuration(
result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"options may be a function object"
);

View File

@ -1,32 +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.duration.prototype.subtract
description: Verify that undefined options are handled correctly.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 24);
const duration3 = new Temporal.Duration(0, 0, 0, 1);
const duration4 = new Temporal.Duration(0, 0, 0, 0, 48);
assert.throws(RangeError, () => duration1.subtract(duration2), "no options with years");
TemporalHelpers.assertDuration(duration3.subtract(duration4),
0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0,
"no options with days");
const optionValues = [
[undefined, "undefined"],
[{}, "plain object"],
[() => {}, "lambda"],
];
for (const [options, description] of optionValues) {
assert.throws(RangeError, () => duration1.subtract(duration2, options),
`options ${description} with years`);
TemporalHelpers.assertDuration(duration3.subtract(duration4, options),
0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0,
`options ${description} with days`);
}

View File

@ -1,23 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: TypeError thrown when options argument is a primitive
features: [BigInt, Symbol, Temporal]
---*/
const badOptions = [
null,
true,
"some string",
Symbol(),
1,
2n,
];
const instance = new Temporal.Duration(0, 0, 0, 0, 1);
for (const value of badOptions) {
assert.throws(TypeError, () => instance.subtract({ hours: 1 }, value),
`TypeError on wrong options type ${typeof value}`);
};

View File

@ -40,8 +40,6 @@ const expected = [
"get fields.years",
"get fields.years.valueOf",
"call fields.years.valueOf",
// ToRelativeTemporalObject
"get options.relativeTo",
];
const actual = [];
@ -58,423 +56,8 @@ const simpleFields = TemporalHelpers.propertyBagObserver(actual, {
nanoseconds: 1,
}, "fields");
function createOptionsObserver(relativeTo = undefined) {
return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options");
}
// basic order of observable operations, without any calendar units:
const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1);
simpleInstance.subtract(simpleFields, createOptionsObserver());
simpleInstance.subtract(simpleFields);
assert.compareArray(actual, expected, "order of operations");
actual.splice(0); // clear
const expectedOpsForPlainRelativeTo = expected.concat([
// ToRelativeTemporalObject
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.microsecond",
"get options.relativeTo.millisecond",
"get options.relativeTo.minute",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.offset",
"get options.relativeTo.second",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
// AddDuration
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.calendar.dateUntil",
]);
const instance = new Temporal.Duration(1, 2, 1, 4, 5, 6, 7, 987, 654, 321);
const fields = TemporalHelpers.propertyBagObserver(actual, {
years: 1,
months: 1,
weeks: 1,
days: 1,
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
year: 2000,
month: 1,
monthCode: "M01",
day: 1,
calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
}, "options.relativeTo");
instance.subtract(fields, createOptionsObserver(plainRelativeTo));
assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
actual.splice(0); // clear
const expectedOpsForPlainRelativeToNoCalendarOperations = [
// ToTemporalDurationRecord
"get fields.days",
"get fields.days.valueOf",
"call fields.days.valueOf",
"get fields.hours",
"get fields.hours.valueOf",
"call fields.hours.valueOf",
"get fields.microseconds",
"get fields.microseconds.valueOf",
"call fields.microseconds.valueOf",
"get fields.milliseconds",
"get fields.milliseconds.valueOf",
"call fields.milliseconds.valueOf",
"get fields.minutes",
"get fields.minutes.valueOf",
"call fields.minutes.valueOf",
"get fields.months",
"get fields.nanoseconds",
"get fields.nanoseconds.valueOf",
"call fields.nanoseconds.valueOf",
"get fields.seconds",
"get fields.seconds.valueOf",
"call fields.seconds.valueOf",
"get fields.weeks",
"get fields.years",
// ToRelativeTemporalObject
"get options.relativeTo",
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.microsecond",
"get options.relativeTo.millisecond",
"get options.relativeTo.minute",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.offset",
"get options.relativeTo.second",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
];
const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
days: 1,
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
noCalendarInstance.subtract(noCalendarFields, createOptionsObserver(plainRelativeTo));
assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
actual.splice(0); // clear
const expectedOpsForZonedRelativeTo = expected.concat([
// ToRelativeTemporalObject
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.hour.valueOf",
"call options.relativeTo.hour.valueOf",
"get options.relativeTo.microsecond",
"get options.relativeTo.microsecond.valueOf",
"call options.relativeTo.microsecond.valueOf",
"get options.relativeTo.millisecond",
"get options.relativeTo.millisecond.valueOf",
"call options.relativeTo.millisecond.valueOf",
"get options.relativeTo.minute",
"get options.relativeTo.minute.valueOf",
"call options.relativeTo.minute.valueOf",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.nanosecond.valueOf",
"call options.relativeTo.nanosecond.valueOf",
"get options.relativeTo.offset",
"get options.relativeTo.offset.toString",
"call options.relativeTo.offset.toString",
"get options.relativeTo.second",
"get options.relativeTo.second.valueOf",
"call options.relativeTo.second.valueOf",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// ToRelativeTemporalObject again
"has options.relativeTo.timeZone.getOffsetNanosecondsFor",
"has options.relativeTo.timeZone.getPossibleInstantsFor",
"has options.relativeTo.timeZone.id",
// InterpretISODateTimeOffset
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
// AddDuration
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// AddDuration → AddZonedDateTime 1
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → AddZonedDateTime 2
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.calendar.dateAdd",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
// AddDuration → DifferenceZonedDateTime
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.calendar.dateUntil",
]);
const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, {
year: 2000,
month: 1,
monthCode: "M01",
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"),
timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"),
}, "options.relativeTo");
instance.subtract(fields, createOptionsObserver(zonedRelativeTo));
assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo");
actual.splice(0); // clear
const expectedOpsForZonedRelativeToNoDaysOperations = [
// ToTemporalDurationRecord
"get fields.days",
"get fields.hours",
"get fields.hours.valueOf",
"call fields.hours.valueOf",
"get fields.microseconds",
"get fields.microseconds.valueOf",
"call fields.microseconds.valueOf",
"get fields.milliseconds",
"get fields.milliseconds.valueOf",
"call fields.milliseconds.valueOf",
"get fields.minutes",
"get fields.minutes.valueOf",
"call fields.minutes.valueOf",
"get fields.months",
"get fields.nanoseconds",
"get fields.nanoseconds.valueOf",
"call fields.nanoseconds.valueOf",
"get fields.seconds",
"get fields.seconds.valueOf",
"call fields.seconds.valueOf",
"get fields.weeks",
"get fields.years",
// ToRelativeTemporalObject
"get options.relativeTo",
"get options.relativeTo.calendar",
"has options.relativeTo.calendar.dateAdd",
"has options.relativeTo.calendar.dateFromFields",
"has options.relativeTo.calendar.dateUntil",
"has options.relativeTo.calendar.day",
"has options.relativeTo.calendar.dayOfWeek",
"has options.relativeTo.calendar.dayOfYear",
"has options.relativeTo.calendar.daysInMonth",
"has options.relativeTo.calendar.daysInWeek",
"has options.relativeTo.calendar.daysInYear",
"has options.relativeTo.calendar.fields",
"has options.relativeTo.calendar.id",
"has options.relativeTo.calendar.inLeapYear",
"has options.relativeTo.calendar.mergeFields",
"has options.relativeTo.calendar.month",
"has options.relativeTo.calendar.monthCode",
"has options.relativeTo.calendar.monthDayFromFields",
"has options.relativeTo.calendar.monthsInYear",
"has options.relativeTo.calendar.weekOfYear",
"has options.relativeTo.calendar.year",
"has options.relativeTo.calendar.yearMonthFromFields",
"has options.relativeTo.calendar.yearOfWeek",
"get options.relativeTo.calendar.dateFromFields",
"get options.relativeTo.calendar.fields",
"call options.relativeTo.calendar.fields",
// PrepareTemporalFields
"get options.relativeTo.day",
"get options.relativeTo.day.valueOf",
"call options.relativeTo.day.valueOf",
"get options.relativeTo.hour",
"get options.relativeTo.hour.valueOf",
"call options.relativeTo.hour.valueOf",
"get options.relativeTo.microsecond",
"get options.relativeTo.microsecond.valueOf",
"call options.relativeTo.microsecond.valueOf",
"get options.relativeTo.millisecond",
"get options.relativeTo.millisecond.valueOf",
"call options.relativeTo.millisecond.valueOf",
"get options.relativeTo.minute",
"get options.relativeTo.minute.valueOf",
"call options.relativeTo.minute.valueOf",
"get options.relativeTo.month",
"get options.relativeTo.month.valueOf",
"call options.relativeTo.month.valueOf",
"get options.relativeTo.monthCode",
"get options.relativeTo.monthCode.toString",
"call options.relativeTo.monthCode.toString",
"get options.relativeTo.nanosecond",
"get options.relativeTo.nanosecond.valueOf",
"call options.relativeTo.nanosecond.valueOf",
"get options.relativeTo.offset",
"get options.relativeTo.offset.toString",
"call options.relativeTo.offset.toString",
"get options.relativeTo.second",
"get options.relativeTo.second.valueOf",
"call options.relativeTo.second.valueOf",
"get options.relativeTo.timeZone",
"get options.relativeTo.year",
"get options.relativeTo.year.valueOf",
"call options.relativeTo.year.valueOf",
// InterpretTemporalDateTimeFields
"call options.relativeTo.calendar.dateFromFields",
// ToRelativeTemporalObject again
"has options.relativeTo.timeZone.getOffsetNanosecondsFor",
"has options.relativeTo.timeZone.getPossibleInstantsFor",
"has options.relativeTo.timeZone.id",
// InterpretISODateTimeOffset
"get options.relativeTo.timeZone.getOffsetNanosecondsFor",
"get options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getPossibleInstantsFor",
"call options.relativeTo.timeZone.getOffsetNanosecondsFor",
// lookup in AddDurationToOrSubtractDurationFromDuration
"get options.relativeTo.calendar.dateAdd",
"get options.relativeTo.calendar.dateUntil",
];
const noDaysInstance = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7, 987, 654, 321);
const noDaysFields = TemporalHelpers.propertyBagObserver(actual, {
hours: 1,
minutes: 1,
seconds: 1,
milliseconds: 1,
microseconds: 1,
nanoseconds: 1,
}, "fields");
noDaysInstance.subtract(noDaysFields, createOptionsObserver(zonedRelativeTo));
assert.compareArray(actual, expectedOpsForZonedRelativeToNoDaysOperations, "order of operations with ZonedDateTime relativeTo and no units above days");
actual.splice(0); // clear

View File

@ -9,9 +9,6 @@ includes: [temporalHelpers.js]
features: [Temporal]
---*/
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
// Largest temporal unit is "day".
const duration1 = Temporal.Duration.from({seconds: 4503599627370495, nanoseconds: 499_999_999});
const duration2 = Temporal.Duration.from({seconds: -4503599627370495 + 86400, nanoseconds: -499_999_999, days: -1});
@ -29,21 +26,3 @@ TemporalHelpers.assertDuration(
Number(nanos % 1000n),
"duration1.subtract(duration2)"
);
TemporalHelpers.assertDuration(
duration1.subtract(duration2, {relativeTo: plainDate}),
0, 0, 0,
Number((nanos / (24n * 60n * 60n * 1_000_000_000n))),
Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n),
Number((nanos / (60n * 1_000_000_000n)) % 60n),
Number((nanos / 1_000_000_000n) % 60n),
Number((nanos / 1_000_000n) % 1000n),
Number((nanos / 1000n) % 1000n),
Number(nanos % 1000n),
"duration1.subtract(duration2, {relativeTo: plainDate})"
);
// Throws a RangeError because the intermediate instant is too large.
assert.throws(RangeError, () => {
duration1.subtract(duration2, {relativeTo: zonedDateTime});
});

View File

@ -1,14 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: If a calendar's fields() method returns a field named '__proto__', PrepareTemporalFields should throw a RangeError.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarWithExtraFields(['__proto__']);
const relativeTo = { year: 2023, month: 5, monthCode: 'M05', day: 1, calendar: calendar, timeZone: 'Europe/Paris' };
const instance = new Temporal.Duration(1, 0, 0, 1);
assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }));

View File

@ -1,22 +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.duration.prototype.subtract
description: The time fields are read from the object before being passed to dateFromFields().
info: |
sec-temporal.duration.prototype.subtract step 5:
5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_).
sec-temporal-torelativetemporalobject step 4.g:
g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_).
sec-temporal-interprettemporaldatetimefields steps 12:
1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_).
2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_).
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarMakeInvalidGettersTime();
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 12);
duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } });

View File

@ -1,23 +0,0 @@
// Copyright (C) 2021 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: Throws if any value in the property bag is Infinity or -Infinity
esid: sec-temporal.duration.prototype.subtract
includes: [compareArray.js, temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 };
[Infinity, -Infinity].forEach((inf) => {
["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => {
assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`);
const calls = [];
const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop);
assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: obj } }));
assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value");
});
});

View File

@ -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.subtract
description: Leap second is constrained in both an ISO string and a property bag
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = "2016-12-31T23:59:60";
const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
TemporalHelpers.assertDuration(
result1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"leap second is a valid ISO string for PlainDate relativeTo"
);
relativeTo = "2016-12-31T23:59:60+00:00[UTC]";
const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
TemporalHelpers.assertDuration(
result2,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"leap second is a valid ISO string for ZonedDateTime relativeTo"
);
relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
TemporalHelpers.assertDuration(
result3,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"second: 60 is valid in a property bag for PlainDate relativeTo"
);
relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" };
const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
TemporalHelpers.assertDuration(
result4,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"second: 60 is valid in a property bag for ZonedDateTime relativeTo"
);

View File

@ -1,18 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: relativeTo with months.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const oneMonth = new Temporal.Duration(0, 1);
const days30 = new Temporal.Duration(0, 0, 0, 30);
TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }),
0, 0, 0, -2, 0, 0, 0, 0, 0, 0, "February");
TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }),
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "March");
TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-04-01') }),
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "April");

View File

@ -1,25 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: A number cannot be used in place of a relativeTo
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
const numbers = [
1,
20191101,
-20191101,
1234567890,
];
for (const relativeTo of numbers) {
assert.throws(
TypeError,
() => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
`A number (${relativeTo}) is not a valid ISO string for relativeTo`
);
}

View File

@ -1,16 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: relativeTo with years.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d1 = new Temporal.Duration(0, 2, 1, 4);
const d2 = new Temporal.Duration(0, 1, 1, 1);
const relativeTo = new Temporal.PlainDate(2000, 1, 1);
TemporalHelpers.assertDuration(d1.subtract(d2, { relativeTo }),
0, 1, 0, 3, 0, 0, 0, 0, 0, 0,
"first this is resolved against relativeTo, then the argument against relativeTo + this");

View File

@ -1,89 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Correct time zone calls are made when converting a ZonedDateTime-like
relativeTo property bag denoting an ambiguous wall-clock time
includes: [temporalHelpers.js, compareArray.js]
features: [Temporal]
---*/
const actual = [];
const dstTimeZone = TemporalHelpers.springForwardFallBackTimeZone();
const dstTimeZoneObserver = TemporalHelpers.timeZoneObserver(actual, "timeZone", {
getOffsetNanosecondsFor: dstTimeZone.getOffsetNanosecondsFor.bind(dstTimeZone),
getPossibleInstantsFor: dstTimeZone.getPossibleInstantsFor.bind(dstTimeZone),
});
const calendar = TemporalHelpers.calendarObserver(actual, "calendar");
const instance = new Temporal.Duration(1, 0, 0, 1);
let relativeTo = { year: 2000, month: 4, day: 2, hour: 2, minute: 30, timeZone: dstTimeZoneObserver, calendar };
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
const expected = [
// GetTemporalCalendarSlotValueWithISODefault
"has calendar.dateAdd",
"has calendar.dateFromFields",
"has calendar.dateUntil",
"has calendar.day",
"has calendar.dayOfWeek",
"has calendar.dayOfYear",
"has calendar.daysInMonth",
"has calendar.daysInWeek",
"has calendar.daysInYear",
"has calendar.fields",
"has calendar.id",
"has calendar.inLeapYear",
"has calendar.mergeFields",
"has calendar.month",
"has calendar.monthCode",
"has calendar.monthDayFromFields",
"has calendar.monthsInYear",
"has calendar.weekOfYear",
"has calendar.year",
"has calendar.yearMonthFromFields",
"has calendar.yearOfWeek",
// lookup
"get calendar.dateFromFields",
"get calendar.fields",
// CalendarFields
"call calendar.fields",
// InterpretTemporalDateTimeFields
"call calendar.dateFromFields",
// ToTemporalTimeZoneSlotValue
"has timeZone.getOffsetNanosecondsFor",
"has timeZone.getPossibleInstantsFor",
"has timeZone.id",
// lookup
"get timeZone.getOffsetNanosecondsFor",
"get timeZone.getPossibleInstantsFor",
// InterpretISODateTimeOffset
"call timeZone.getPossibleInstantsFor",
];
const expectedSpringForward = expected.concat([
// DisambiguatePossibleInstants
"call timeZone.getOffsetNanosecondsFor",
"call timeZone.getOffsetNanosecondsFor",
"call timeZone.getPossibleInstantsFor",
]);
assert.compareArray(
actual.slice(0, expectedSpringForward.length), // ignore operations after ToRelativeTemporalObject
expectedSpringForward,
"order of operations converting property bag at skipped wall-clock time"
);
actual.splice(0); // clear
relativeTo = { year: 2000, month: 10, day: 29, hour: 1, minute: 30, timeZone: dstTimeZoneObserver, calendar };
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
assert.compareArray(
actual.slice(0, expected.length), // ignore operations after ToRelativeTemporalObject
expected,
"order of operations converting property bag at repeated wall-clock time"
);
actual.splice(0); // clear

View File

@ -1,22 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Calling the method with a relativeTo property bag with a builtin calendar
causes no observable array iteration when getting the calendar fields.
features: [Temporal]
---*/
const arrayPrototypeSymbolIteratorOriginal = Array.prototype[Symbol.iterator];
Array.prototype[Symbol.iterator] = function arrayIterator() {
throw new Test262Error("Array should not be iterated");
}
const timeZone = "UTC";
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, hour: 21, minute: 43, second: 5, timeZone, calendar: "iso8601" };
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
Array.prototype[Symbol.iterator] = arrayPrototypeSymbolIteratorOriginal;

View File

@ -1,16 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Calendar.dateFromFields method is called with a null-prototype fields object
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution();
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, calendar };
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar");

View File

@ -1,26 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: A number as calendar in relativeTo property bag is invalid
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 0, 0, 1);
const numbers = [
1,
19970327,
-19970327,
1234567890,
];
for (const calendar of numbers) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(
TypeError,
() => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
`A number (${calendar}) is not a valid ISO string for relativeTo.calendar`
);
}

View File

@ -1,26 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Builtin dateFromFields method is not observably called when the property bag
has a string-valued calendar property
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const dateFromFieldsOriginal = Object.getOwnPropertyDescriptor(Temporal.Calendar.prototype, "dateFromFields");
Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", {
configurable: true,
enumerable: false,
get() {
TemporalHelpers.assertUnreachable("dateFromFields should not be looked up");
},
});
const instance = new Temporal.Duration(1, 0, 0, 1);
const relativeTo = { year: 2000, month: 5, day: 2, calendar: "iso8601" };
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
Object.defineProperty(Temporal.Calendar.prototype, "dateFromFields", dateFromFieldsOriginal);

View File

@ -1,45 +0,0 @@
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Appropriate error thrown when relativeTo.calendar cannot be converted to a
calendar object or string
features: [BigInt, Symbol, Temporal]
---*/
const timeZone = new Temporal.TimeZone("UTC");
const instance = new Temporal.Duration(1, 0, 0, 1);
const primitiveTests = [
[null, "null"],
[true, "boolean"],
["", "empty string"],
[1, "number that doesn't convert to a valid ISO string"],
[1n, "bigint"],
];
for (const [calendar, description] of primitiveTests) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(
typeof calendar === 'string' ? RangeError : TypeError,
() => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }),
`${description} does not convert to a valid ISO string`
);
}
const typeErrorTests = [
[Symbol(), "symbol"],
[{}, "plain object that doesn't implement the protocol"],
[new Temporal.TimeZone("UTC"), "time zone instance"],
[Temporal.PlainDate, "Temporal.PlainDate, object"],
[Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"],
[Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"],
[Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"],
];
for (const [calendar, description] of typeErrorTests) {
const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`);
}

View File

@ -1,56 +0,0 @@
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: >
Time zone's getPossibleInstantsFor is called with a PlainDateTime with the
built-in ISO 8601 calendar
features: [Temporal]
info: |
DisambiguatePossibleInstants:
2. Let _n_ be _possibleInstants_'s length.
...
5. Assert: _n_ = 0.
...
19. If _disambiguation_ is *"earlier"*, then
...
c. Let _earlierDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
d. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _earlierDateTime_).
...
20. Assert: _disambiguation_ is *"compatible"* or *"later"*.
...
23. Let _laterDateTime_ be ! CreateTemporalDateTime(..., *"iso8601"*).
24. Set _possibleInstants_ to ? GetPossibleInstantsFor(_timeZone_, _laterDateTime_).
---*/
class SkippedDateTime extends Temporal.TimeZone {
constructor() {
super("UTC");
this.calls = 0;
}
getPossibleInstantsFor(dateTime) {
// Calls occur in pairs. For the first one return no possible instants so
// that DisambiguatePossibleInstants will call it again
if (this.calls++ % 2 == 0) {
return [];
}
assert.sameValue(
dateTime.getISOFields().calendar,
"iso8601",
"getPossibleInstantsFor called with dateTime with built-in ISO 8601 calendar"
);
return super.getPossibleInstantsFor(dateTime);
}
}
const nonBuiltinISOCalendar = new Temporal.Calendar("iso8601");
const timeZone = new SkippedDateTime();
const relativeTo = { year: 2000, month: 5, day: 2, timeZone, calendar: nonBuiltinISOCalendar };
const instance = new Temporal.Duration(1, 0, 0, 1);
instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo });
assert.sameValue(timeZone.calls, 6, "getPossibleInstantsFor should have been called 6 times");

Some files were not shown because too many files have changed in this diff Show More