Merge branch 'main' into web-features-init

This commit is contained in:
jugglinmike 2025-11-03 16:26:11 -05:00 committed by GitHub
commit c61bcf856f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
199 changed files with 6515 additions and 130 deletions

View File

@ -290,6 +290,7 @@ Function | Purpose
`assert.sameValue(actual, expected, message)` | throw a new Test262Error instance if the first two arguments are not [the same value](https://tc39.github.io/ecma262/#sec-samevalue); accepts an optional string message explaining the scenario and what should have happened
`assert.notSameValue(actual, unexpected, message)` | throw a new Test262Error instance if the first two arguments are [the same value](https://tc39.github.io/ecma262/#sec-samevalue); accepts an optional string message explaining the scenario and what should have happened
`assert.throws(expectedErrorConstructor, fn, message)` | throw a new Test262Error instance if the provided function does not throw an error or if the constructor of the value thrown does not match the provided constructor; accepts an optional string message explaining the scenario and what should have happened
`assert.compareArray(actual, expected, message)` | throw a new Test262Error instance if the first two arguments have differing `length` or there is an array index less than that length at which their respective elements are not [the same value](https://tc39.github.io/ecma262/#sec-samevalue); accepts an optional string message explaining the scenario and what should have happened
`$DONOTEVALUATE()` | throw an exception if the code gets evaluated. This may only be used in [negative test cases for parsing errors](#handling-errors-and-negative-test-cases).
`throw "Test262: This statement should not be evaluated.";` | throw an exception if the code gets evaluated. Use this if the test file has the `raw` flag and it's a negative test case for parsing error.

View File

@ -11,6 +11,10 @@
#
# https://github.com/tc39/process-document
# Intl Era Monthcode
# https://github.com/tc39/proposal-intl-era-monthcode
Intl.Era-monthcode
# Intl.Locale Info
# https://github.com/tc39/proposal-intl-locale-info
Intl.Locale-info
@ -50,20 +54,12 @@ json-parse-with-source
# https://github.com/tc39/proposal-explicit-resource-management
explicit-resource-management
# Math.sumPrecise
# https://github.com/tc39/proposal-math-sum
Math.sumPrecise
# Source Phase Imports
## https://github.com/tc39/proposal-source-phase-imports
source-phase-imports
## test262 special specifier
source-phase-imports-module-source
# Uint8Array Base64
# https://github.com/tc39/proposal-arraybuffer-base64
uint8array-base64
# Atomics.pause
# https://github.com/tc39/proposal-atomics-microwait
Atomics.pause
@ -184,6 +180,7 @@ json-superset
let
logical-assignment-operators
Map
Math.sumPrecise
new.target
numeric-separator-literal
object-rest
@ -255,6 +252,7 @@ TypedArray
TypedArray.prototype.at
u180e
Uint8Array
uint8array-base64
Uint16Array
Uint32Array
Uint8ClampedArray

View File

@ -101,6 +101,45 @@ assert.throws = function (expectedErrorConstructor, func, message) {
throw new Test262Error(message);
};
function isPrimitive(value) {
return !value || (typeof value !== 'object' && typeof value !== 'function');
}
assert.compareArray = function (actual, expected, message) {
message = message === undefined ? '' : message;
if (typeof message === 'symbol') {
message = message.toString();
}
if (isPrimitive(actual)) {
assert(false, `Actual argument [${actual}] shouldn't be primitive. ${message}`);
} else if (isPrimitive(expected)) {
assert(false, `Expected argument [${expected}] shouldn't be primitive. ${message}`);
}
var result = compareArray(actual, expected);
if (result) return;
var format = compareArray.format;
assert(false, `Actual ${format(actual)} and expected ${format(expected)} should have the same contents. ${message}`);
};
function compareArray(a, b) {
if (b.length !== a.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!assert._isSameValue(b[i], a[i])) {
return false;
}
}
return true;
}
compareArray.format = function (arrayLike) {
return `[${Array.prototype.map.call(arrayLike, String).join(', ')}]`;
};
assert._formatIdentityFreeValue = function formatIdentityFreeValue(value) {
switch (value === null ? 'null' : typeof value) {
case 'string':

View File

@ -2,49 +2,6 @@
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
Compare the contents of two arrays
Deprecated now that compareArray is defined in assert.js.
defines: [compareArray]
---*/
function compareArray(a, b) {
if (b.length !== a.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!compareArray.isSameValue(b[i], a[i])) {
return false;
}
}
return true;
}
compareArray.isSameValue = function(a, b) {
if (a === 0 && b === 0) return 1 / a === 1 / b;
if (a !== a && b !== b) return true;
return a === b;
};
compareArray.format = function(arrayLike) {
return `[${[].map.call(arrayLike, String).join(', ')}]`;
};
assert.compareArray = function(actual, expected, message) {
message = message === undefined ? '' : message;
if (typeof message === 'symbol') {
message = message.toString();
}
assert(actual != null, `Actual argument shouldn't be nullish. ${message}`);
assert(expected != null, `Expected argument shouldn't be nullish. ${message}`);
var format = compareArray.format;
var result = compareArray(actual, expected);
// The following prevents actual and expected from being iterated and evaluated
// more than once unless absolutely necessary.
if (!result) {
assert(false, `Actual ${format(actual)} and expected ${format(expected)} should have the same contents. ${message}`);
}
};

View File

@ -1,5 +1,5 @@
/*---
defines: [assertNear]
defines: [assertNear, ONE_PLUS_EPSILON, ONE_MINUS_EPSILON]
---*/
// The nearest representable values to +1.0.
@ -50,10 +50,20 @@ const ONE_MINUS_EPSILON = 1 - Math.pow(2, -53); // 1.0000000000000002
ENDIAN = 0; // try little-endian first
if (diff(2, 4) === 0x100000) // exact wrong answer we'll get on a big-endian platform
ENDIAN = 1;
assert.sameValue(diff(2,4), 0x10000000000000);
assert.sameValue(diff(0, Number.MIN_VALUE), 1);
assert.sameValue(diff(1, ONE_PLUS_EPSILON), 1);
assert.sameValue(diff(1, ONE_MINUS_EPSILON), 1);
// For test262-harness compatibility,
// avoid `assert.sameValue` while still defining functions.
// https://github.com/bocoup/test262-stream/issues/34
const assertDiffResult = (a, b, expect, detail) => {
const result = diff(a, b);
if (result === expect) return;
throw new Error(
`Expected diff(${a}, ${b}) to be ${expect} but got ${result} [${detail}]`
);
};
assertDiffResult(2, 4, 0x10000000000000, "wanted 0x10000000000000");
assertDiffResult(0, Number.MIN_VALUE, 1, "0 vs. Number.MIN_VALUE");
assertDiffResult(1, ONE_PLUS_EPSILON, 1, "1 vs. ONE_PLUS_EPSILON");
assertDiffResult(1, ONE_MINUS_EPSILON, 1, "1 vs. ONE_MINUS_EPSILON");
var assertNear = function assertNear(a, b, tolerance=1) {
if (!Number.isFinite(b)) {

View File

@ -0,0 +1,105 @@
// Copyright 2015 Microsoft Corporation. All rights reserved.
// This code is governed by the license found in the LICENSE file.
/*---
esid: sec-object.assign
description: >
Object.assign with an Array Exotic Object target employs the corresponding
internal methods.
info: |
Object.assign ( _target_, ..._sources_ )
3.a.iii.2.b. Perform ? Set(_to_, _nextKey_, _propValue_, *true*).
Set ( _O_, _P_, _V_, _Throw_ )
1. Let _success_ be ? _O_.[[Set]](_P_, _V_, _O_).
OrdinarySet ( _O_, _P_, _V_, _Receiver_ )
1. Let _ownDesc_ be ? _O_.[[GetOwnProperty]](_P_).
2. Return ? OrdinarySetWithOwnDescriptor(_O_, _P_, _V_, _Receiver_, _ownDesc_).
OrdinarySetWithOwnDescriptor ( _O_, _P_, _V_, _Receiver_, _ownDesc_ )
1. If _ownDesc_ is *undefined*, then
a. Let _parent_ be ? O.[[GetPrototypeOf]]().
b. If _parent_ is not *null*, then
i. Return ? _parent_.[[Set]](_P_, _V_, _Receiver_).
c. Else,
i. Set _ownDesc_ to the PropertyDescriptor { [[Value]]: *undefined*, [[Writable]]: *true*, [[Enumerable]]: *true*, [[Configurable]]: *true* }.
2. If IsDataDescriptor(_ownDesc_) is *true*, then
...
c. Let _existingDescriptor_ be ? _Receiver_.[[GetOwnProperty]](_P_).
d. If _existingDescriptor_ is not *undefined*, then
...
iii. Let _valueDesc_ be the PropertyDescriptor { [[Value]]: _V_ }.
iv. Return ? _Receiver_.[[DefineOwnProperty]](_P_, _valueDesc_).
e. Else,
i. Assert: _Receiver_ does not currently have a property _P_.
ii. Return ? CreateDataProperty(_Receiver_, _P_, _V_).
CreateDataProperty ( _O_, _P_, _V_ )
1. Let _newDesc_ be the PropertyDescriptor { [[Value]]: _V_, [[Writable]]: *true*, [[Enumerable]]: *true*, [[Configurable]]: *true* }.
2. Return ? _O_.[[DefineOwnProperty]](_P_, _newDesc_).
Array exotic object [[DefineOwnProperty]] ( _P_, _Desc_ )
1. If _P_ is *"length"*, then
a. Return ? ArraySetLength(_A_, _Desc_).
2. Else if _P_ is an array index, then
...
k. If _index_ _length_, then
i. Set _lengthDesc_.[[Value]] to _index_ + *1*𝔽.
ii. Set _succeeded_ to ! OrdinaryDefineOwnProperty(_A_, *"length"*, _lengthDesc_).
3. Return ? OrdinaryDefineOwnProperty(_A_, _P_, _Desc_).
The Object Type
An **integer index** is a property name _n_ such that CanonicalNumericIndexString(_n_) returns an
integral Number in the inclusive interval from *+0*𝔽 to 𝔽(2**53 - 1). An **array index** is an
integer index _n_ such that CanonicalNumericIndexString(_n_) returns an integral Number in the
inclusive interval from *+0*𝔽 to 𝔽(2**32 - 2).
---*/
var target = [7, 8, 9];
var result = Object.assign(target, [1]);
assert.sameValue(result, target);
assert.compareArray(result, [1, 8, 9],
"elements must be assigned from an array source onto an array target");
var sparseArraySource = [];
sparseArraySource[2] = 3;
result = Object.assign(target, sparseArraySource);
assert.sameValue(result, target);
assert.compareArray(result, [1, 8, 3], "holes in a sparse array source must not be copied");
var shortObjectSource = { 1: 2, length: 2 };
shortObjectSource["-0"] = -1;
shortObjectSource["1.5"] = -2;
shortObjectSource["4294967295"] = -3; // 2**32 - 1
result = Object.assign(target, shortObjectSource);
assert.sameValue(result, target);
assert.compareArray(result, [1, 2],
"array index properties must be copied from a non-array source");
assert.sameValue(result["-0"], -1,
"a property with name -0 must be assigned onto an array target");
assert.sameValue(result["1.5"], -2,
"a property with name 1.5 must be assigned onto an array target");
assert.sameValue(result["4294967295"], -3,
"a property with name 4294967295 (2**32 - 1) must be assigned onto an array target");
result = Object.assign(target, { length: 1 });
assert.sameValue(result, target);
assert.compareArray(result, [1], "assigning a short length must shrink an array target");
result = Object.assign(target, { 2: 0 });
assert.sameValue(result, target);
assert.compareArray(result, [1, undefined, 0],
"assigning a high array index must grow an array target");
if (typeof Proxy !== 'undefined') {
var accordionSource = new Proxy({ length: 0, 1: 9 }, {
ownKeys: function() {
return ["length", "1"];
}
});
result = Object.assign(target, accordionSource);
assert.sameValue(result, target);
assert.compareArray(result, [undefined, 9],
"assigning a short length before a high array index must shrink and then grow an array target");
}

View File

@ -11,6 +11,8 @@ features: [promise-with-resolvers]
var instance = Promise.withResolvers();
assert.sameValue(typeof instance.resolve, 'function', 'type of resolve property');
assert.sameValue(instance.resolve.name, "");
assert.sameValue(instance.resolve.length, 1, 'length of resolve property');
assert.sameValue(typeof instance.reject, 'function', 'type of reject property');
assert.sameValue(instance.reject.name, "");
assert.sameValue(instance.reject.length, 1, 'length of reject property');

View File

@ -0,0 +1,29 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.compare
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = Temporal.Duration.compare(oneProperty, oneProperty);
const resultWith = Temporal.Duration.compare(allProperties, allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,37 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.compare
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const duration1 = new Temporal.Duration(1);
const duration2 = new Temporal.Duration(0, 1);
let relativeTo = {
year: 2021,
month: 10,
day: 28,
timeZone: "UTC",
};
const resultWithout = Temporal.Duration.compare(duration1, duration2, { relativeTo });
relativeTo = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone: "UTC",
calendar: "iso8601",
};
const resultWith = Temporal.Duration.compare(duration1, duration2, { relativeTo });
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,30 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.from
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = Temporal.Duration.from(oneProperty);
const resultWith = Temporal.Duration.from(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,32 @@
// Copyright (C) 2025 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 property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration();
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,19 @@
// Copyright (C) 2025 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: Internal representation uses float64-representable integers
features: [Temporal]
---*/
const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, /* µs = */ Number.MAX_SAFE_INTEGER, 0);
const result = d.add({ microseconds: Number.MAX_SAFE_INTEGER - 1 });
// (𝔽(18014398509481981)) = 18014398509481980
assert.sameValue(result.microseconds, 18014398509481980,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18014398509.48198S",
"toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent add() should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,24 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: Internal representation uses float64-representable integers
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, /* ms = */ 18014398509481, /* µs = */ 981, 0);
const result = d.round({ largestUnit: "microseconds" });
// (𝔽(18014398509481981)) = 18014398509481980
assert.sameValue(result.microseconds, 18014398509481980,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18014398509.48198S",
"toString() should not use more precise internal representation than the spec prescribes");
// Rounding bounds of 8 µs are ...976 and ...984. halfTrunc will round down if
// the µs component is ...980 and up if it is ...981
TemporalHelpers.assertDuration(
result.round({ largestUnit: "seconds", smallestUnit: "microseconds", roundingMode: "halfTrunc", roundingIncrement: 8 }),
0, 0, 0, 0, 0, 0, 18014398509, 481, 976, 0,
"subsequent round() should not use more precise internal representation than the spec prescribes");

View File

@ -14,7 +14,7 @@ const instance = new Temporal.Duration(1, 0, 0, 0, 24);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -32,6 +32,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.round({ largestUnit: "years", relativeTo }),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -24,6 +24,6 @@ badOffsets.forEach((offset) => {
assert.throws(
typeof(offset) === 'string' ? RangeError : TypeError,
() => instance.round({ largestUnit: "years", relativeTo }),
`"${offset} is not a valid offset string`
`"${offset}" is not a valid offset string`
);
});

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.round
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = "UTC";
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let relativeTo = {
year: 2021,
month: 10,
day: 28,
timeZone,
};
const resultWithout = instance.round({ largestUnit: "years", relativeTo });
relativeTo = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone,
calendar: "iso8601",
};
const resultWith = instance.round({ largestUnit: "years", relativeTo });
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -17,7 +17,7 @@ const primitiveTests = [
[null, 'null'],
[true, 'boolean'],
['', 'empty string'],
[1, "number that doesn't convert to a valid ISO string"],
[1, 'number'],
[1n, 'bigint']
];

View File

@ -0,0 +1,32 @@
// Copyright (C) 2025 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 property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.Duration();
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,19 @@
// Copyright (C) 2025 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: Internal representation uses float64-representable integers
features: [Temporal]
---*/
const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, /* µs = */ Number.MAX_SAFE_INTEGER, 0);
const result = d.subtract({ microseconds: Number.MIN_SAFE_INTEGER + 1 });
// (𝔽(18014398509481981)) = 18014398509481980
assert.sameValue(result.microseconds, 18014398509481980,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18014398509.48198S",
"toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.subtract({ microseconds: 1 }), result), 0,
"subsequent subtract() should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.tojson
description: Pairs of units with one large and one small
features: [Temporal]
---*/
assert.sameValue(
new Temporal.Duration(1, 0, 0, 0, 0, 0, 0, 0, 0, 1).toJSON(),
"P1YT0.000000001S",
"years with nanoseconds"
);
assert.sameValue(
new Temporal.Duration(0, 1, 0, 0, 0, 0, 0, 0, 1).toJSON(),
"P1MT0.000001S",
"months with microseconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 1, 0, 0, 0, 0, 1).toJSON(),
"P1WT0.001S",
"weeks with milliseconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 0, 1, 0, 0, 1).toJSON(),
"P1DT1S",
"days with seconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 0, 0, 1, 1).toJSON(),
"PT1H1M",
"hours with minutes"
);

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.tostring
description: Pairs of units with one large and one small
features: [Temporal]
---*/
assert.sameValue(
new Temporal.Duration(1, 0, 0, 0, 0, 0, 0, 0, 0, 1).toString(),
"P1YT0.000000001S",
"years with nanoseconds"
);
assert.sameValue(
new Temporal.Duration(0, 1, 0, 0, 0, 0, 0, 0, 1).toString(),
"P1MT0.000001S",
"months with microseconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 1, 0, 0, 0, 0, 1).toString(),
"P1WT0.001S",
"weeks with milliseconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 0, 1, 0, 0, 1).toString(),
"P1DT1S",
"days with seconds"
);
assert.sameValue(
new Temporal.Duration(0, 0, 0, 0, 1, 1).toString(),
"PT1H1M",
"hours with minutes"
);

View File

@ -0,0 +1,37 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.total
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const timeZone = "UTC";
const instance = new Temporal.Duration(1, 0, 0, 0, 24);
let relativeTo = {
year: 2021,
month: 10,
day: 28,
timeZone,
};
const resultWithout = instance.total({ unit: "days", relativeTo });
relativeTo = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone,
calendar: "iso8601",
};
const resultWith = instance.total({ unit: "days", relativeTo });
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -17,7 +17,7 @@ const primitiveTests = [
[null, 'null'],
[true, 'boolean'],
['', 'empty string'],
[1, "number that doesn't convert to a valid ISO string"],
[1, 'number'],
[1n, 'bigint']
];

View File

@ -0,0 +1,52 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: Adding unbalanced durations with large subsecond values to an instant
features: [Temporal]
---*/
const i1 = new Temporal.Instant(1582966647747612578n);
assert.sameValue(i1.add(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
1591973847002353569n);
assert.sameValue(i1.add(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
1573959448492871587n);
assert.sameValue(i1.add(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
10590165902488603578n);
assert.sameValue(i1.add(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
-7424232606993378422n);
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
assert.sameValue(i1.add(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
10590165902488603554n);
assert.sameValue(i1.add(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
-7424232606993378398n);
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({milliseconds: -bigNumber})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => i1.add(Temporal.Duration.from({microseconds: -bigNumber})));
const i2 = new Temporal.Instant(0n);
assert.sameValue(i2.add(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
9007199254740990976n);
assert.sameValue(i2.add(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
-9007199254740990976n);
assert.throws(RangeError, () => i2.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => i2.add(Temporal.Duration.from({milliseconds: -bigNumber})));
assert.throws(RangeError, () => i2.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => i2.add(Temporal.Duration.from({microseconds: -bigNumber})));

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.since
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const i1 = new Temporal.Instant(0n);
const i2 = new Temporal.Instant(18446744073_709_551_616n);
const result = i1.since(i2, { largestUnit: "microseconds" });
// (𝔽(-18446744073709551)) = -18446744073709552
assert.sameValue(result.microseconds, -18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "-PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.Instant(0n);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,52 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: Subtracting unbalanced durations with large subsecond values from a date
features: [Temporal]
---*/
const i1 = new Temporal.Instant(1582966647747612578n);
assert.sameValue(i1.subtract(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
1573959448492871587n);
assert.sameValue(i1.subtract(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
1591973847002353569n);
assert.sameValue(i1.subtract(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
-7424232606993378422n);
assert.sameValue(i1.subtract(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
10590165902488603578n);
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
assert.sameValue(i1.subtract(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
-7424232606993378398n);
assert.sameValue(i1.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
10590165902488603554n);
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => i1.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));
const i2 = new Temporal.Instant(0n);
assert.sameValue(i2.subtract(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
-9007199254740990976n);
assert.sameValue(i2.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
9007199254740990976n);
assert.throws(RangeError, () => i2.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => i2.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => i2.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => i2.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));

View File

@ -0,0 +1,12 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.tostring
description: Epoch milliseconds should be rounded down before adding negative micro/nanoseconds back in
features: [BigInt, Temporal]
---*/
const instant = new Temporal.Instant(-1000000000000001000n);
assert.sameValue(instant.toString(), "1938-04-24T22:13:19.999999Z",
"epoch milliseconds should be rounded down to compute seconds");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.until
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const i1 = new Temporal.Instant(0n);
const i2 = new Temporal.Instant(18446744073_709_551_616n);
const result = i1.until(i2, { largestUnit: "microseconds" });
// (𝔽(18446744073709551)) = 18446744073709552
assert.sameValue(result.microseconds, 18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(1970, 1, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: Adding months to maximum year should throw
features: [Temporal]
---*/
const maxYear = new Temporal.PlainDate(275760, 1, 1);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => maxYear.add(duration));
const minYear = new Temporal.PlainDate(-271821, 4, 19);
assert.throws(RangeError, () => minYear.add(duration.negated()));

View File

@ -5,7 +5,7 @@
esid: sec-temporal.plaindate.prototype.since
description: >
Appropriate error thrown when a calendar property from a property bag cannot
be converted to a calendar object
be converted to a calendar ID
features: [BigInt, Symbol, Temporal]
---*/

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(1970, 1, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: Subtracting months from minimum year should throw
features: [Temporal]
---*/
const minYear = new Temporal.PlainDate(-271821, 4, 19);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => minYear.subtract(duration));
const maxYear = new Temporal.PlainDate(275760, 1, 1);
assert.throws(RangeError, () => maxYear.subtract(duration.negated()));

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.toplaindatetime
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.toPlainDateTime(minimumProperties);
const resultWith = instance.toPlainDateTime(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.tozoneddatetime
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.toZonedDateTime({ plainTime: minimumProperties, timeZone: "UTC" });
const resultWith = instance.toZonedDateTime({ plainTime: allProperties, timeZone: "UTC" });
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.compare
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
calendar: "iso8601",
};
const resultWithout = Temporal.PlainDateTime.compare(minimumProperties, minimumProperties);
const resultWith = Temporal.PlainDateTime.compare(allProperties, allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.from
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
calendar: "iso8601",
};
const resultWithout = Temporal.PlainDateTime.from(minimumProperties);
const resultWith = Temporal.PlainDateTime.from(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,53 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: Adding unbalanced durations with large subsecond values to a date
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const pdt1 = new Temporal.PlainDateTime(2020, 2, 29, 0, 57, 27, 747, 612, 578);
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})),
2020, 6, "M06", 12, 6, 57, 27, 2, 353, 569);
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})),
2019, 11, "M11", 16, 18, 57, 28, 492, 871, 587);
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})),
2305, 8, "M08", 4, 0, 45, 2, 488, 603, 578);
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})),
1734, 9, "M09", 26, 1, 9, 53, 6, 621, 578);
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({nanoseconds: bigNumber})),
2305, 8, "M08", 4, 0, 45, 2, 488, 603, 554);
TemporalHelpers.assertPlainDateTime(pdt1.add(Temporal.Duration.from({nanoseconds: -bigNumber})),
1734, 9, "M09", 26, 1, 9, 53, 6, 621, 602);
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => pdt1.add(Temporal.Duration.from({milliseconds: -bigNumber})));
const pdt2 = new Temporal.PlainDateTime(0, 1, 1);
TemporalHelpers.assertPlainDateTime(pdt2.add(Temporal.Duration.from({nanoseconds: bigNumber})),
285, 6, "M06", 4, 23, 47, 34, 740, 990, 976);
TemporalHelpers.assertPlainDateTime(pdt2.add(Temporal.Duration.from({nanoseconds: -bigNumber})),
-286, 7, "M07", 29, 0, 12, 25, 259, 9, 24);
assert.throws(RangeError, () => pdt2.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => pdt2.add(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => pdt2.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => pdt2.add(Temporal.Duration.from({milliseconds: -bigNumber})));

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(1970, 1, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: Adding months to maximum year should throw
features: [Temporal]
---*/
const maxYear = new Temporal.PlainDateTime(275760, 1, 1);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => maxYear.add(duration));
const minYear = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
assert.throws(RangeError, () => minYear.add(duration.negated()));

View File

@ -0,0 +1,33 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.equals
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
calendar: "iso8601",
};
const resultWithout = instance.equals(minimumProperties);
const resultWith = instance.equals(allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -5,7 +5,7 @@
esid: sec-temporal.plaindatetime.prototype.since
description: >
Appropriate error thrown when a calendar property from a property bag cannot
be converted to a calendar object or string
be converted to a calendar ID
features: [BigInt, Symbol, Temporal]
---*/

View File

@ -0,0 +1,34 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.since
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
calendar: "iso8601",
};
const resultWithout = instance.since(minimumProperties);
const resultWith = instance.since(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.since
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const dt1 = new Temporal.PlainDateTime(1970, 1, 1);
const dt2 = new Temporal.PlainDateTime(2554, 7, 21, 23, 34, 33, 709, 551, 616);
const result = dt1.since(dt2, { largestUnit: "microseconds" });
// (𝔽(-18446744073709551)) = -18446744073709552
assert.sameValue(result.microseconds, -18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "-PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(1970, 1, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: Subtracting months from minimum year should throw
features: [Temporal]
---*/
const minYear = new Temporal.PlainDateTime(-271821, 4, 19, 0, 0, 0, 0, 0, 1);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => minYear.subtract(duration));
const maxYear = new Temporal.PlainDateTime(275760, 1, 1);
assert.throws(RangeError, () => maxYear.subtract(duration.negated()));

View File

@ -0,0 +1,53 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: Subtracting unbalanced durations with large subsecond values from a date
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const pdt1 = new Temporal.PlainDateTime(2020, 2, 29, 0, 57, 27, 747, 612, 578);
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})),
2019, 11, "M11", 16, 18, 57, 28, 492, 871, 587);
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})),
2020, 6, "M06", 12, 6, 57, 27, 2, 353, 569);
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})),
1734, 9, "M09", 26, 1, 9, 53, 6, 621, 578);
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})),
2305, 8, "M08", 4, 0, 45, 2, 488, 603, 578);
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({nanoseconds: bigNumber})),
1734, 9, "M09", 26, 1, 9, 53, 6, 621, 602);
TemporalHelpers.assertPlainDateTime(pdt1.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})),
2305, 8, "M08", 4, 0, 45, 2, 488, 603, 554);
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => pdt1.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));
const pdt2 = new Temporal.PlainDateTime(0, 1, 1);
TemporalHelpers.assertPlainDateTime(pdt2.subtract(Temporal.Duration.from({nanoseconds: bigNumber})),
-286, 7, "M07", 29, 0, 12, 25, 259, 9, 24);
TemporalHelpers.assertPlainDateTime(pdt2.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})),
285, 6, "M06", 4, 23, 47, 34, 740, 990, 976);
assert.throws(RangeError, () => pdt2.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => pdt2.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => pdt2.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => pdt2.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));

View File

@ -0,0 +1,12 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.tostring
description: Epoch milliseconds should be rounded down before adding negative micro/nanoseconds back in
features: [Temporal]
---*/
const pdt = new Temporal.PlainDateTime(1938, 4, 24, 22, 13, 19, 999, 999);
assert.sameValue(pdt.toString(), "1938-04-24T22:13:19.999999",
"epoch milliseconds should be rounded down to compute seconds");

View File

@ -0,0 +1,34 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.until
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
calendar: "iso8601",
};
const resultWithout = instance.until(minimumProperties);
const resultWith = instance.until(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.until
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const dt1 = new Temporal.PlainDateTime(1970, 1, 1);
const dt2 = new Temporal.PlainDateTime(2554, 7, 21, 23, 34, 33, 709, 551, 616);
const result = dt1.until(dt2, { largestUnit: "microseconds" });
// (𝔽(18446744073709551)) = 18446744073709552
assert.sameValue(result.microseconds, 18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -14,7 +14,7 @@ const instance = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456,
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -28,6 +28,6 @@ for (const [arg, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.withCalendar(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.withplaintime
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.withPlainTime(minimumProperties);
const resultWith = instance.withPlainTime(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -9,7 +9,9 @@ features: [Temporal]
["m1", "M1", "m01"].forEach((monthCode) => {
assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ monthCode, day: 17 }),
`monthCode '${monthCode}' is not well-formed`);
`monthCode '${monthCode}' is not well-formed (without numeric month)`);
assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ month: 1, monthCode, day: 17 }),
`monthCode '${monthCode}' is not well-formed (with numeric month)`);
});
assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ year: 2021, month: 12, monthCode: "M11", day: 17 }),
@ -17,7 +19,19 @@ assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ year: 2021, month:
["M00", "M19", "M99", "M13", "M00L", "M05L", "M13L"].forEach((monthCode) => {
assert.throws(RangeError, () => Temporal.PlainMonthDay.from({ monthCode, day: 17 }),
`monthCode '${monthCode}' is not valid for ISO 8601 calendar`);
`monthCode '${monthCode}' is not valid for ISO 8601 calendar (without numeric month)`);
var monthNumber = Number(monthCode.slice(1, 3)) + (monthCode.length - 3);
assert.throws(
RangeError,
() => Temporal.PlainMonthDay.from({ month: monthNumber, monthCode, day: 17 }),
`monthCode '${monthCode}' is not valid for ISO 8601 calendar (with numeric month)`
);
var clampedMonthNumber = monthNumber < 1 ? 1 : monthNumber > 12 ? 12 : monthNumber;
assert.throws(
RangeError,
() => Temporal.PlainMonthDay.from({ month: clampedMonthNumber, monthCode, day: 17 }),
`monthCode '${monthCode}' is not valid for ISO 8601 calendar (with clamped numeric month)`
);
});
assert.throws(

View File

@ -14,7 +14,7 @@ const instance = new Temporal.PlainMonthDay(5, 2);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -29,6 +29,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.equals(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.compare
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = Temporal.PlainTime.compare(minimumProperties, minimumProperties);
const resultWith = Temporal.PlainTime.compare(allProperties, allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,25 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.from
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = Temporal.PlainTime.from(minimumProperties);
const resultWith = Temporal.PlainTime.from(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,65 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: Adding unbalanced durations with large subsecond values to a time
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const t1 = new Temporal.PlainTime(0, 57, 27, 747, 612, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})),
6, 57, 27, 2, 353, 569);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})),
18, 57, 28, 492, 871, 587);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})),
0, 45, 2, 488, 603, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})),
1, 9, 53, 6, 621, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})),
9, 56, 28, 738, 612, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})),
15, 58, 26, 756, 612, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})),
8, 33, 58, 747, 612, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})),
17, 20, 56, 747, 612, 578);
const bigNumber = 9007199254740990976;
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({nanoseconds: bigNumber})),
0, 45, 2, 488, 603, 554);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({nanoseconds: -bigNumber})),
1, 9, 53, 6, 621, 602);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({microseconds: bigNumber})),
9, 56, 28, 738, 588, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({microseconds: -bigNumber})),
15, 58, 26, 756, 636, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({milliseconds: bigNumber})),
8, 33, 58, 723, 612, 578);
TemporalHelpers.assertPlainTime(t1.add(Temporal.Duration.from({milliseconds: -bigNumber})),
17, 20, 56, 771, 612, 578);
const t2 = new Temporal.PlainTime(0);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({nanoseconds: bigNumber})),
23, 47, 34, 740, 990, 976);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({nanoseconds: -bigNumber})),
0, 12, 25, 259, 9, 24);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({microseconds: bigNumber})),
8, 59, 0, 990, 976, 0);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({microseconds: -bigNumber})),
15, 0, 59, 9, 24, 0);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({milliseconds: bigNumber})),
7, 36, 30, 976, 0, 0);
TemporalHelpers.assertPlainTime(t2.add(Temporal.Duration.from({milliseconds: -bigNumber})),
16, 23, 29, 24, 0, 0);

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainTime();
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.equals
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.equals(minimumProperties);
const resultWith = instance.equals(allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.since
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.since(minimumProperties);
const resultWith = instance.since(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainTime();
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,65 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.subtract
description: Subtracting unbalanced durations with large subsecond values from a time
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const t1 = new Temporal.PlainTime(0, 57, 27, 747, 612, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})),
18, 57, 28, 492, 871, 587);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})),
6, 57, 27, 2, 353, 569);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})),
1, 9, 53, 6, 621, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})),
0, 45, 2, 488, 603, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})),
15, 58, 26, 756, 612, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})),
9, 56, 28, 738, 612, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})),
17, 20, 56, 747, 612, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})),
8, 33, 58, 747, 612, 578);
const bigNumber = 9007199254740990976;
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({nanoseconds: bigNumber})),
1, 9, 53, 6, 621, 602);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})),
0, 45, 2, 488, 603, 554);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({microseconds: bigNumber})),
15, 58, 26, 756, 636, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({microseconds: -bigNumber})),
9, 56, 28, 738, 588, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({milliseconds: bigNumber})),
17, 20, 56, 771, 612, 578);
TemporalHelpers.assertPlainTime(t1.subtract(Temporal.Duration.from({milliseconds: -bigNumber})),
8, 33, 58, 723, 612, 578);
const t2 = new Temporal.PlainTime(0);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({nanoseconds: bigNumber})),
0, 12, 25, 259, 9, 24);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})),
23, 47, 34, 740, 990, 976);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({microseconds: bigNumber})),
15, 0, 59, 9, 24, 0);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({microseconds: -bigNumber})),
8, 59, 0, 990, 976, 0);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({milliseconds: bigNumber})),
16, 23, 29, 24, 0, 0);
TemporalHelpers.assertPlainTime(t2.subtract(Temporal.Duration.from({milliseconds: -bigNumber})),
7, 36, 30, 976, 0, 0);

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.until
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.until(minimumProperties);
const resultWith = instance.until(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainYearMonth(1970, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.add
description: Adding months to maximum year should throw
features: [Temporal]
---*/
const maxYear = new Temporal.PlainYearMonth(275760, 1);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => maxYear.add(duration));
const minYear = new Temporal.PlainYearMonth(-271821, 4);
assert.throws(RangeError, () => minYear.add(duration.negated()));

View File

@ -14,7 +14,7 @@ const instance = new Temporal.PlainYearMonth(2000, 5);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large positive number"],
[-19970327, "negative number"],
@ -29,6 +29,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.equals(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -5,7 +5,7 @@
esid: sec-temporal.plainyearmonth.prototype.since
description: >
Appropriate error thrown when a calendar property from a property bag cannot
be converted to a calendar object or string
be converted to a calendar ID
features: [BigInt, Symbol, Temporal]
---*/
@ -14,7 +14,7 @@ const instance = new Temporal.PlainYearMonth(2000, 5);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large positive number"],
[-19970327, "negative number"],
@ -29,6 +29,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.since(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.PlainYearMonth(1970, 1);
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainyearmonth.prototype.subtract
description: Subtracting months from minimum year should throw
features: [Temporal]
---*/
const minYear = new Temporal.PlainYearMonth(-271821, 4);
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => minYear.subtract(duration));
const maxYear = new Temporal.PlainYearMonth(275760, 1);
assert.throws(RangeError, () => maxYear.subtract(duration.negated()));

View File

@ -14,7 +14,7 @@ const instance = new Temporal.PlainYearMonth(2000, 5);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large positive number"],
[-19970327, "large negative number"],
@ -29,6 +29,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.until(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.compare
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
timeZone: "UTC",
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone: "UTC",
calendar: "iso8601",
};
const resultWithout = Temporal.ZonedDateTime.compare(minimumProperties, minimumProperties);
const resultWith = Temporal.ZonedDateTime.compare(allProperties, allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,34 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.from
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
timeZone: "UTC",
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone: "UTC",
calendar: "iso8601",
};
const resultWithout = Temporal.ZonedDateTime.from(minimumProperties);
const resultWith = Temporal.ZonedDateTime.from(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,53 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.add
description: Adding unbalanced durations with large subsecond values to a datetime
features: [Temporal]
---*/
// const pdt1 = new Temporal.PlainDateTime(2020, 2, 29, 0, 57, 27, 747, 612, 578);
const zdt1 = new Temporal.ZonedDateTime(1582966647747612578n, "America/Los_Angeles");
assert.sameValue(zdt1.add(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
1591973847002353569n);
assert.sameValue(zdt1.add(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
1573959448492871587n);
assert.sameValue(zdt1.add(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
10590165902488603578n);
assert.sameValue(zdt1.add(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
-7424232606993378422n);
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
assert.sameValue(zdt1.add(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
10590165902488603554n);
assert.sameValue(zdt1.add(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
-7424232606993378398n);
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({milliseconds: -bigNumber})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => zdt1.add(Temporal.Duration.from({microseconds: -bigNumber})));
const zdt2 = new Temporal.ZonedDateTime(0n, "UTC");
assert.sameValue(zdt2.add(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
9007199254740990976n);
assert.sameValue(zdt2.add(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
-9007199254740990976n);
assert.throws(RangeError, () => zdt2.add(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => zdt2.add(Temporal.Duration.from({milliseconds: -bigNumber})));
assert.throws(RangeError, () => zdt2.add(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => zdt2.add(Temporal.Duration.from({microseconds: -bigNumber})));

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.add
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.ZonedDateTime(0n, "UTC");
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.add(oneProperty);
const resultWith = instance.add(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.add
description: Adding months to maximum year should throw
features: [Temporal]
---*/
const maxYear = new Temporal.PlainDate(275760, 1, 1).toZonedDateTime("UTC");
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => maxYear.add(duration));
const minYear = new Temporal.ZonedDateTime(-(864n * 10n ** 19n), "UTC");
assert.throws(RangeError, () => minYear.add(duration.negated()));

View File

@ -12,10 +12,10 @@ features: [BigInt, Symbol, Temporal]
const timeZone = "UTC";
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const primitiveTests = [
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -25,11 +25,11 @@ const primitiveTests = [
[new Temporal.Duration(), "duration instance"],
];
for (const [calendar, description] of primitiveTests) {
for (const [calendar, description] of wrongTypeTests) {
const arg = { year: 2019, monthCode: "M11", day: 1, calendar };
assert.throws(
TypeError,
() => instance.equals(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.equals
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const timeZone = "UTC";
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
timeZone,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone,
calendar: "iso8601",
};
const resultWithout = instance.equals(minimumProperties);
const resultWith = instance.equals(allProperties);
assert.sameValue(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -5,7 +5,7 @@
esid: sec-temporal.zoneddatetime.prototype.since
description: >
Appropriate error thrown when a calendar property from a property bag cannot
be converted to a calendar object or string
be converted to a calendar ID
features: [BigInt, Symbol, Temporal]
---*/
@ -15,7 +15,7 @@ const instance = new Temporal.ZonedDateTime(0n, timeZone);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -30,6 +30,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.since(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = "UTC";
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
timeZone,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone,
calendar: "iso8601",
};
const resultWithout = instance.since(minimumProperties);
const resultWith = instance.since(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.since
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const z1 = new Temporal.ZonedDateTime(0n, "UTC");
const z2 = new Temporal.ZonedDateTime(18446744073_709_551_616n, "UTC");
const result = z1.since(z2, { largestUnit: "microseconds" });
// (𝔽(-18446744073709551)) = -18446744073709552
assert.sameValue(result.microseconds, -18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "-PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.subtract
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.ZonedDateTime(0n, "UTC");
const oneProperty = {
hours: 1,
};
const allProperties = {
years: 0,
months: 0,
weeks: 0,
days: 0,
hours: 1,
minutes: 0,
seconds: 0,
milliseconds: 0,
microseconds: 0,
nanoseconds: 0,
};
const resultWithout = instance.subtract(oneProperty);
const resultWith = instance.subtract(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -0,0 +1,15 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.subtract
description: Subtracting months from minimum year should throw
features: [Temporal]
---*/
const minYear = new Temporal.ZonedDateTime(-(864n * 10n ** 19n), "UTC");
const duration = new Temporal.Duration(0, 5432, 5432, 0, 0, 0, 0, 0, 0, 0);
assert.throws(RangeError, () => minYear.subtract(duration));
const maxYear = new Temporal.PlainDateTime(275760, 1, 1).toZonedDateTime("UTC");
assert.throws(RangeError, () => maxYear.subtract(duration.negated()));

View File

@ -0,0 +1,53 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.subtract
description: Subtracting unbalanced durations with large subsecond values from a date
features: [Temporal]
---*/
// const pdt1 = new Temporal.PlainDateTime(2020, 2, 29, 0, 57, 27, 747, 612, 578);
const zdt1 = new Temporal.ZonedDateTime(1582966647747612578n, "America/Los_Angeles");
assert.sameValue(zdt1.subtract(Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
1573959448492871587n);
assert.sameValue(zdt1.subtract(Temporal.Duration.from({nanoseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
1591973847002353569n);
assert.sameValue(zdt1.subtract(Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER})).epochNanoseconds,
-7424232606993378422n);
assert.sameValue(zdt1.subtract(Temporal.Duration.from({microseconds: Number.MIN_SAFE_INTEGER})).epochNanoseconds,
10590165902488603578n);
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({milliseconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({milliseconds: Number.MIN_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({seconds: Number.MAX_SAFE_INTEGER})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({seconds: Number.MIN_SAFE_INTEGER})));
const bigNumber = 9007199254740990976;
assert.sameValue(zdt1.subtract(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
-7424232606993378398n);
assert.sameValue(zdt1.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
10590165902488603554n);
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => zdt1.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));
const zdt2 = new Temporal.ZonedDateTime(0n, "UTC");
assert.sameValue(zdt2.subtract(Temporal.Duration.from({nanoseconds: bigNumber})).epochNanoseconds,
-9007199254740990976n);
assert.sameValue(zdt2.subtract(Temporal.Duration.from({nanoseconds: -bigNumber})).epochNanoseconds,
9007199254740990976n);
assert.throws(RangeError, () => zdt2.subtract(Temporal.Duration.from({microseconds: bigNumber})));
assert.throws(RangeError, () => zdt2.subtract(Temporal.Duration.from({microseconds: -bigNumber})));
assert.throws(RangeError, () => zdt2.subtract(Temporal.Duration.from({milliseconds: bigNumber})));
assert.throws(RangeError, () => zdt2.subtract(Temporal.Duration.from({milliseconds: -bigNumber})));

View File

@ -0,0 +1,12 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.tostring
description: Epoch milliseconds should be rounded down before adding negative micro/nanoseconds back in
features: [BigInt, Temporal]
---*/
const zdt = new Temporal.ZonedDateTime(-1000000000000001000n, "UTC");
assert.sameValue(zdt.toString(), "1938-04-24T22:13:19.999999+00:00[UTC]",
"epoch milliseconds should be rounded down to compute seconds");

View File

@ -15,7 +15,7 @@ const instance = new Temporal.ZonedDateTime(0n, timeZone);
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[19970327, "large number"],
[-19970327, "negative number"],
@ -30,6 +30,6 @@ for (const [calendar, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.until(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const timeZone = "UTC";
const instance = new Temporal.ZonedDateTime(0n, timeZone);
const minimumProperties = {
year: 2021,
month: 10,
day: 28,
timeZone,
};
const allProperties = {
year: 2021,
month: 10,
day: 28,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
offset: "+00:00",
timeZone,
calendar: "iso8601",
};
const resultWithout = instance.until(minimumProperties);
const resultWith = instance.until(allProperties);
TemporalHelpers.assertDurationsEqual(resultWithout, resultWith, "results should be the same with and without optional properties");

View File

@ -0,0 +1,20 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.until
description: Internal representation of Duration uses float64-representable integers
features: [Temporal]
---*/
const z1 = new Temporal.ZonedDateTime(0n, "UTC");
const z2 = new Temporal.ZonedDateTime(18446744073_709_551_616n, "UTC");
const result = z1.until(z2, { largestUnit: "microseconds" });
// (𝔽(18446744073709551)) = 18446744073709552
assert.sameValue(result.microseconds, 18446744073709552,
"microseconds result should have FP precision loss");
assert.sameValue(result.toString(), "PT18446744073.709552616S",
"Duration.p.toString() should not use more precise internal representation than the spec prescribes");
assert.sameValue(Temporal.Duration.compare(result.add({ microseconds: 1 }), result), 0,
"subsequent ops on duration should not use more precise internal representation than the spec prescribes");

View File

@ -4,7 +4,8 @@
/*---
esid: sec-temporal.zoneddatetime.prototype.withcalendar
description: >
Appropriate error thrown when argument cannot be converted to a valid object or string
Appropriate error thrown when argument cannot be converted to a valid string
for Calendar
features: [BigInt, Symbol, Temporal]
---*/
@ -13,7 +14,7 @@ const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", "
const wrongTypeTests = [
[null, "null"],
[true, "boolean"],
[1, "number that doesn't convert to a valid ISO string"],
[1, "number"],
[1n, "bigint"],
[-19761118, "negative number"],
[19761118, "large positive number"],
@ -27,6 +28,6 @@ for (const [arg, description] of wrongTypeTests) {
assert.throws(
TypeError,
() => instance.withCalendar(arg),
`${description} does not convert to a valid ISO string`
`${description} is not a valid calendar`
);
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zoneddatetime.prototype.withplaintime
description: >
A property bag missing optional properties is equivalent to a property bag
with all the optional properties having their default values
features: [Temporal]
---*/
const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC");
const minimumProperties = {
hour: 0,
};
const allProperties = {
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const resultWithout = instance.withPlainTime(minimumProperties);
const resultWith = instance.withPlainTime(allProperties);
assert(resultWithout.equals(resultWith), "results should be the same with and without optional properties");

View File

@ -17,8 +17,15 @@ var localArgs = function() {
"use strict";
return arguments;
}();
var otherArgs = (new other.Function('return arguments;'))();
var otherArgs = (new other.Function('"use strict"; return arguments;'))();
var otherArgs2 = (new other.Function('"use strict"; return arguments;'))();
var localThrowTypeError = Object.getOwnPropertyDescriptor(localArgs, "callee").get;
var otherThrowTypeError = Object.getOwnPropertyDescriptor(otherArgs, "callee").get;
var otherThrowTypeError2 = Object.getOwnPropertyDescriptor(otherArgs, "callee").get;
assert.throws(TypeError, function() {
otherThrowTypeError();
});
assert.notSameValue(localThrowTypeError, otherThrowTypeError);
assert.sameValue(otherThrowTypeError, otherThrowTypeError2);

View File

@ -0,0 +1,48 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-uint8array.prototype.setfrombase64
description: >
Garbage input is ignored when the typed array has a zero length
info: |
Uint8Array.prototype.setFromBase64 ( string [ , options ] )
...
13. Let byteLength be TypedArrayLength(taRecord).
14. Let result be FromBase64(string, alphabet, lastChunkHandling, byteLength).
...
FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
...
3. If maxLength = 0, then
a. Return the Record { [[Read]]: 0, [[Bytes]]: « », [[Error]]: none }.
...
features: [uint8array-base64, TypedArray]
---*/
// Zero length typed array.
var u8 = new Uint8Array(0);
// No SyntaxError when passing invalid inputs.
for (var string of [
"#",
"a#",
"aa#",
"aaa#",
"aaaa#",
]) {
for (var lastChunkHandling of ["loose", "strict", "stop-before-partial"]) {
var result = u8.setFromBase64(string, {lastChunkHandling});
assert.sameValue(
result.read,
0,
`Read for "${string}" with lastChunkHandling="${lastChunkHandling}"`
);
assert.sameValue(
result.written,
0,
`Write for "${string}" with lastChunkHandling="${lastChunkHandling}"`
);
}
}

View File

@ -0,0 +1,83 @@
// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-uint8array.prototype.setfrombase64
description: >
Trailing garbage is ignored when no more space is left in the output
info: |
Uint8Array.prototype.setFromBase64 ( string [ , options ] )
...
13. Let byteLength be TypedArrayLength(taRecord).
14. Let result be FromBase64(string, alphabet, lastChunkHandling, byteLength).
...
FromBase64 ( string, alphabet, lastChunkHandling [ , maxLength ] )
...
10. Repeat,
...
l. If chunkLength = 4, then
...
v. If the number of elements in bytes = maxLength, then
1. Return the Record { [[Read]]: read, [[Bytes]]: bytes, [[Error]]: none }.
features: [uint8array-base64, TypedArray]
---*/
// Uint8Array large enough to hold a single decoded chunk.
var u8 = new Uint8Array(3);
// Throws a SyntaxError for incomplete chunks.
for (var invalid of [
"#",
"a#",
"aa#",
"aaa#",
]) {
for (var lastChunkHandling of ["loose", "strict", "stop-before-partial"]) {
assert.throws(SyntaxError, function() {
u8.setFromBase64(invalid, {lastChunkHandling});
}, `"${invalid}" is rejected with lastChunkHandling="${lastChunkHandling}"`);
}
}
// No SyntaxError when a full chunk can be read.
for (var valid of [
"aaaa#",
"aaaaa#",
"aaaaaa#",
"aaaaaaa#",
"aaaaaaaa#",
]) {
for (var lastChunkHandling of ["loose", "strict", "stop-before-partial"]) {
// Reset state.
u8.fill(0);
var result = u8.setFromBase64(valid, {lastChunkHandling});
assert.sameValue(
result.read,
4,
`Read for "${valid}" with lastChunkHandling="${lastChunkHandling}"`
);
assert.sameValue(
result.written,
3,
`Write for "${valid}" with lastChunkHandling="${lastChunkHandling}"`
);
assert.sameValue(
u8[0],
0x69,
`Index=0 for "${valid}" with lastChunkHandling="${lastChunkHandling}"`
);
assert.sameValue(
u8[1],
0xa6,
`Index=1 for "${valid}" with lastChunkHandling="${lastChunkHandling}"`
);
assert.sameValue(
u8[2],
0x9a,
`Index=2 for "${valid}" with lastChunkHandling="${lastChunkHandling}"`
);
}
}

View File

@ -20,9 +20,9 @@ function assertThrows(func, errorMessage) {
assert(caught, `Expected ${func} to throw, but it didn't.`);
}
assertThrows(() => assert.compareArray(), "Actual argument shouldn't be nullish. ");
assertThrows(() => assert.compareArray(null, []), "Actual argument shouldn't be nullish. ");
assertThrows(() => assert.compareArray(null, [], "foo"), "Actual argument shouldn't be nullish. foo");
assertThrows(() => assert.compareArray(), "Actual argument [undefined] shouldn't be primitive. ");
assertThrows(() => assert.compareArray(null, []), "Actual argument [null] shouldn't be primitive. ");
assertThrows(() => assert.compareArray(null, [], "foo"), "Actual argument [null] shouldn't be primitive. foo");
assertThrows(() => assert.compareArray([]), "Expected argument shouldn't be nullish. ");
assertThrows(() => assert.compareArray([], undefined, "foo"), "Expected argument shouldn't be nullish. foo");
assertThrows(() => assert.compareArray([]), "Expected argument [undefined] shouldn't be primitive. ");
assertThrows(() => assert.compareArray([], undefined, "foo"), "Expected argument [undefined] shouldn't be primitive. foo");

View File

@ -0,0 +1,51 @@
// Copyright (C) 2025 Igalia S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-intl.supportedvaluesof
description: >
Verifies that all calendars required by Intl.Era-monthcode are supported. See:
https://tc39.es/proposal-intl-era-monthcode/#table-calendar-types
info: |
Intl.supportedValuesOf ( key )
1 Let key be ? ToString(key).
2. If key is "calendar", then
a. Let list be a new empty List.
b. For each element identifier of AvailableCalendars(), do
i. Let canonical be CanonicalizeUValue("ca", identifier).
ii. If identifier is canonical, then
1. Append identifier to list.
...
9. Return CreateArrayFromList( list ).
AvailableCalendars ( )
The implementation-defined abstract operation AvailableCalendars takes no arguments and returns a List of calendar types. The returned List is sorted according to lexicographic code unit order, and contains unique calendar types in canonical form (6.9) identifying the calendars for which the implementation provides the functionality of Intl.DateTimeFormat objects, including their aliases (e.g., both of "islamicc" and "islamic-civil"). The List must include the Calendar Type value of every row of Table 1, except the header row.
locale: [en]
features: [Intl-enumeration, Intl.Era-monthcode]
---*/
const requiredCalendars = [
"buddhist",
"chinese",
"coptic",
"dangi",
"ethioaa",
"ethiopic",
"ethiopic-amete-alem",
"gregory",
"hebrew",
"indian",
"islamic-civil",
"islamic-tbla",
"islamic-umalqura",
"islamicc",
"iso8601",
"japanese",
"persian",
"roc"
]
const supportedCalendars = Intl.supportedValuesOf("calendar");
for (const calendar of requiredCalendars) {
assert(supportedCalendars.includes(calendar), "Required calendar: " + calendar + " must be supported");
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal-calendarsupportsera
description: Calendar era code is canonicalized (non-Gregorian calendars)
features: [Temporal, Intl.Era-monthcode]
---*/
const calendarEraAliases = [
{ calendar: "japanese", canonicalizedEra: "ce", alias: "ad" },
{ calendar: "japanese", canonicalizedEra: "bce", alias: "bc" }
];
for (const calendarEraAlias of calendarEraAliases) {
const calendar = Temporal.PlainDate.from({
calendar: calendarEraAlias.calendar,
era: calendarEraAlias.alias,
eraYear: 1,
month: 1,
day: 1
});
assert.sameValue(calendar.era, calendarEraAlias.canonicalizedEra, calendar.era + " should canonicalize to " + calendarEraAlias.canonicalizedEra)
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.from
description: Calendar era code is canonicalized (non-Gregorian calendars)
features: [Temporal, Intl.Era-monthcode]
---*/
const calendarEraAliases = [
{ calendar: "japanese", canonicalizedEra: "ce", alias: "ad" },
{ calendar: "japanese", canonicalizedEra: "bce", alias: "bc" }
];
for (const calendarEraAlias of calendarEraAliases) {
const calendar = Temporal.PlainDateTime.from({
calendar: calendarEraAlias.calendar,
era: calendarEraAlias.alias,
eraYear: 1,
month: 1,
day: 1
});
assert.sameValue(calendar.era, calendarEraAlias.canonicalizedEra, calendar.era + " should canonicalize to " + calendarEraAlias.canonicalizedEra)
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal-plainyearmonth.from
description: Calendar era code is canonicalized (non-Gregorian calendars)
features: [Temporal, Intl.Era-monthcode]
---*/
const calendarEraAliases = [
{ calendar: "japanese", canonicalizedEra: "ce", alias: "ad" },
{ calendar: "japanese", canonicalizedEra: "bce", alias: "bc" }
];
for (const calendarEraAlias of calendarEraAliases) {
const calendar = Temporal.PlainYearMonth.from({
calendar: calendarEraAlias.calendar,
era: calendarEraAlias.alias,
eraYear: 1,
month: 1,
day: 1
});
assert.sameValue(calendar.era, calendarEraAlias.canonicalizedEra, calendar.era + " should canonicalize to " + calendarEraAlias.canonicalizedEra)
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.zonedatetime.from
description: Calendar era code is canonicalized (non-Gregorian calendars)
features: [Temporal, Intl.Era-monthcode]
---*/
const calendarEraAliases = [
{ calendar: "japanese", canonicalizedEra: "ce", alias: "ad" },
{ calendar: "japanese", canonicalizedEra: "bce", alias: "bc" }
];
for (const calendarEraAlias of calendarEraAliases) {
const calendar = Temporal.ZonedDateTime.from({
calendar: calendarEraAlias.calendar,
era: calendarEraAlias.alias,
eraYear: 1,
month: 1,
day: 1,
timeZone: "UTC"
});
assert.sameValue(calendar.era, calendarEraAlias.canonicalizedEra, calendar.era + " should canonicalize to " + calendarEraAlias.canonicalizedEra)
}

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