summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since')
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js53
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js29
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js40
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js32
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js43
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js46
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js23
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js51
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js0
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js36
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js20
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js30
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js26
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js95
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js21
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js37
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js27
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js61
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js25
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js49
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js28
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js17
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js41
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js93
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js39
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js24
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js22
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js19
-rw-r--r--js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js25
75 files changed, 2467 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js
new file mode 100644
index 0000000000..b53b7be74b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-cast.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Casts the argument
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const plainTime = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+TemporalHelpers.assertDuration(plainTime.since("16:34"),
+ 0, 0, 0, 0, /* hours = */ -1, /* minutes = */ -10, /* seconds = */ -29, -876, -543, -211, "string");
+TemporalHelpers.assertDuration(plainTime.since({ hour: 16, minute: 34 }),
+ 0, 0, 0, 0, /* hours = */ -1, /* minutes = */ -10, /* seconds = */ -29, -876, -543, -211, "object");
+
+assert.throws(TypeError, () => plainTime.since({}), "empty");
+assert.throws(TypeError, () => plainTime.since({ minutes: 30 }), "only plural 'minutes'");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js
new file mode 100644
index 0000000000..df9cd66d25
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-number.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: A number is invalid in place of an ISO string for Temporal.PlainTime
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const numbers = [
+ 1,
+ -123456.987654321,
+ 1234567,
+ 123456.9876543219,
+];
+
+for (const arg of numbers) {
+ assert.throws(
+ TypeError,
+ () => instance.since(arg),
+ `A number (${arg}) is not a valid ISO string for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js
new file mode 100644
index 0000000000..ece0c996a0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of calendar annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[u-ca=iso8601]", "without time zone"],
+ ["12:34:56.987654321[UTC][u-ca=iso8601]", "with time zone"],
+ ["12:34:56.987654321[!u-ca=iso8601]", "with ! and no time zone"],
+ ["12:34:56.987654321[UTC][!u-ca=iso8601]", "with ! and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601]", "with T and no time zone"],
+ ["T12:34:56.987654321[UTC][u-ca=iso8601]", "with T and time zone"],
+ ["T12:34:56.987654321[!u-ca=iso8601]", "with T, !, and no time zone"],
+ ["T12:34:56.987654321[UTC][!u-ca=iso8601]", "with T, !, and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601]", "with date and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][u-ca=iso8601]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[!u-ca=iso8601]", "with !, date, and no time zone"],
+ ["1970-01-01T12:34:56.987654321[UTC][!u-ca=iso8601]", "with !, date, and time zone"],
+ ["12:34:56.987654321[u-ca=hebrew]", "calendar annotation ignored"],
+ ["12:34:56.987654321[u-ca=unknown]", "calendar annotation ignored even if unknown calendar"],
+ ["12:34:56.987654321[!u-ca=unknown]", "calendar annotation ignored even if unknown calendar with !"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][u-ca=discord]", "second annotation ignored"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `calendar annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js
new file mode 100644
index 0000000000..84eb98a585
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-critical-unknown-annotation.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Unknown annotations with critical flag are rejected
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[!foo=bar]",
+ "T00:00[!foo=bar]",
+ "1970-01-01T00:00[!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar]",
+ "1970-01-01T00:00[u-ca=iso8601][!foo=bar]",
+ "1970-01-01T00:00[UTC][!foo=bar][u-ca=iso8601]",
+ "1970-01-01T00:00[foo=bar][!_foo-bar0=Dont-Ignore-This-99999999999]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject unknown annotation with critical flag: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js
new file mode 100644
index 0000000000..83a2cb8f80
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-date-with-utc-offset.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: UTC offset not valid with format that does not include a time
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const validStrings = [
+ "12:34:56.987654321+00:00",
+ "12:34:56.987654321+00:00[UTC]",
+ "12:34:56.987654321+00:00[!UTC]",
+ "12:34:56.987654321-02:30[America/St_Johns]",
+ "1976-11-18T12:34:56.987654321+00:00",
+ "1976-11-18T12:34:56.987654321+00:00[UTC]",
+ "1976-11-18T12:34:56.987654321+00:00[!UTC]",
+ "1976-11-18T12:34:56.987654321-02:30[America/St_Johns]",
+];
+
+for (const arg of validStrings) {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `"${arg}" is a valid UTC offset with time for PlainTime`
+ );
+}
+
+const invalidStrings = [
+ "2022-09-15Z",
+ "2022-09-15Z[UTC]",
+ "2022-09-15Z[Europe/Vienna]",
+ "2022-09-15+00:00",
+ "2022-09-15+00:00[UTC]",
+ "2022-09-15-02:30",
+ "2022-09-15-02:30[America/St_Johns]",
+];
+
+for (const arg of invalidStrings) {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `"${arg}" UTC offset without time is not valid for PlainTime`
+ );
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js
new file mode 100644
index 0000000000..02cb57b271
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-calendar.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ More than one calendar annotation is not syntactical if any have the criical
+ flag
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[!u-ca=iso8601][u-ca=iso8601]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][!u-ca=iso8601]",
+ "1970-01-01T00:00[u-ca=iso8601][foo=bar][!u-ca=iso8601]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one calendar annotation if any critical: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js
new file mode 100644
index 0000000000..6a50d1787f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-multiple-time-zone.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: More than one time zone annotation is not syntactical
+features: [Temporal]
+---*/
+
+const invalidStrings = [
+ "00:00[UTC][UTC]",
+ "T00:00[UTC][UTC]",
+ "1970-01-01T00:00[UTC][UTC]",
+ "1970-01-01T00:00[!UTC][UTC]",
+ "1970-01-01T00:00[UTC][!UTC]",
+ "1970-01-01T00:00[UTC][u-ca=iso8601][UTC]",
+ "1970-01-01T00:00[UTC][foo=bar][UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `reject more than one time zone annotation: ${arg}`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js
new file mode 100644
index 0000000000..0f4f71046c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-no-implicit-midnight.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if a date-only string is passed in a PlainTime context
+features: [Temporal, arrow-function]
+---*/
+
+const arg = "2019-10-01";
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "Date-only string throws, does not implicitly convert to midnight"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js
new file mode 100644
index 0000000000..28da5edc0e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: ISO 8601 time designator "T" required in cases of ambiguity
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+TemporalHelpers.ISO.plainTimeStringsAmbiguous().forEach((string) => {
+ let arg = string;
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `'${arg}' is ambiguous and requires T prefix`
+ );
+ // The same string with a T prefix should not throw:
+ arg = `T${string}`;
+ instance.since(arg);
+
+ arg = ` ${string}`;
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ `space is not accepted as a substitute for T prefix: '${arg}'`
+ );
+});
+
+// None of these should throw without a T prefix, because they are unambiguously time strings:
+TemporalHelpers.ISO.plainTimeStringsUnambiguous().forEach(
+ (arg) => instance.since(arg));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js
new file mode 100644
index 0000000000..252d7a6e53
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-separators.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Time separator in string argument can vary
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["1976-11-18T12:34:56.987654321", "uppercase T"],
+ ["1976-11-18t12:34:56.987654321", "lowercase T"],
+ ["1976-11-18 12:34:56.987654321", "space between date and time"],
+ ["T12:34:56.987654321", "time-only uppercase T"],
+ ["t12:34:56.987654321", "time-only lowercase T"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `variant time separators (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js
new file mode 100644
index 0000000000..3f75d12f81
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of time zone annotation; critical flag has no effect
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[Asia/Kolkata]", "named, with no offset"],
+ ["12:34:56.987654321[!Europe/Vienna]", "named, with ! and no offset"],
+ ["12:34:56.987654321[+00:00]", "numeric, with no offset"],
+ ["12:34:56.987654321[!-02:30]", "numeric, with ! and no offset"],
+ ["T12:34:56.987654321[UTC]", "named, with T and no offset"],
+ ["T12:34:56.987654321[!Africa/Abidjan]", "named, with T, !, and no offset"],
+ ["T12:34:56.987654321[+01:00]", "numeric, with T and no offset"],
+ ["T12:34:56.987654321[!-08:00]", "numeric, with T, !, and no offset"],
+ ["12:34:56.987654321+00:00[America/Sao_Paulo]", "named, with offset"],
+ ["12:34:56.987654321+00:00[!Asia/Tokyo]", "named, with ! and offset"],
+ ["12:34:56.987654321+00:00[-02:30]", "numeric, with offset"],
+ ["12:34:56.987654321+00:00[!+00:00]", "numeric, with ! and offset"],
+ ["T12:34:56.987654321+00:00[America/New_York]", "named, with T and offset"],
+ ["T12:34:56.987654321+00:00[!UTC]", "named, with T, !, and offset"],
+ ["T12:34:56.987654321+00:00[-08:00]", "numeric, with T and offset"],
+ ["T12:34:56.987654321+00:00[!+01:00]", "numeric, with T, !, and offset"],
+ ["1970-01-01T12:34:56.987654321[Africa/Lagos]", "named, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!America/Vancouver]", "named, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321[+00:00]", "numeric, with date and no offset"],
+ ["1970-01-01T12:34:56.987654321[!-02:30]", "numeric, with date, !, and no offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[Europe/London]", "named, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!Asia/Seoul]", "named, with date, offset, and !"],
+ ["1970-01-01T12:34:56.987654321+00:00[+01:00]", "numeric, with date and offset"],
+ ["1970-01-01T12:34:56.987654321+00:00[!-08:00]", "numeric, with date, offset, and !"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `time zone annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js
new file mode 100644
index 0000000000..1c610e5c72
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Various forms of unknown annotation
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const tests = [
+ ["12:34:56.987654321[foo=bar]", "alone"],
+ ["12:34:56.987654321[UTC][foo=bar]", "with time zone"],
+ ["12:34:56.987654321[u-ca=iso8601][foo=bar]", "with calendar"],
+ ["12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with time zone and calendar"],
+ ["T12:34:56.987654321[foo=bar]", "with T"],
+ ["T12:34:56.987654321[UTC][foo=bar]", "with T and time zone"],
+ ["T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with T and calendar"],
+ ["T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with T, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar]", "with date"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar]", "with date and time zone"],
+ ["1970-01-01T12:34:56.987654321[u-ca=iso8601][foo=bar]", "with date and calendar"],
+ ["1970-01-01T12:34:56.987654321[UTC][foo=bar][u-ca=iso8601]", "with date, time zone, and calendar"],
+ ["1970-01-01T12:34:56.987654321[foo=bar][_foo-bar0=Ignore-This-999999999999]", "with another unknown annotation"],
+];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+tests.forEach(([arg, description]) => {
+ const result = instance.since(arg);
+
+ TemporalHelpers.assertDuration(
+ result,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ `unknown annotation (${description})`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js
new file mode 100644
index 0000000000..6a1ac52bfb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-time-designator.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: ISO 8601 time designator "T" allowed at the start of PlainTime strings
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+const validStrings = [
+ "T00:30",
+ "t00:30",
+ "T0030",
+ "t0030",
+ "T00:30:00",
+ "t00:30:00",
+ "T003000",
+ "t003000",
+ "T00:30:00.000000000",
+ "t00:30:00.000000000",
+ "T003000.000000000",
+ "t003000.000000000",
+];
+validStrings.forEach((arg) => {
+ const result = instance.since(arg);
+ TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 30, 0, 0, 0, 1, `T prefix is accepted: ${arg}`);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js
new file mode 100644
index 0000000000..c7325625ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-string-with-utc-designator.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if a string with UTC designator is used as a PlainTime
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "2019-10-01T09:00:00Z",
+ "2019-10-01T09:00:00Z[UTC]",
+ "09:00:00Z[UTC]",
+ "09:00:00Z",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "String with UTC designator should not be valid as a PlainTime"
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js
new file mode 100644
index 0000000000..7aaa3324f5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-wrong-type.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Appropriate error thrown when argument cannot be converted to a valid string
+ or property bag for PlainTime
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const primitiveTests = [
+ [undefined, "undefined"],
+ [null, "null"],
+ [true, "boolean"],
+ ["", "empty string"],
+ [1, "number that doesn't convert to a valid ISO string"],
+ [1n, "bigint"],
+];
+
+for (const [arg, description] of primitiveTests) {
+ assert.throws(
+ typeof arg === 'string' ? RangeError : TypeError,
+ () => instance.since(arg),
+ `${description} does not convert to a valid ISO string`
+ );
+}
+
+const typeErrorTests = [
+ [Symbol(), "symbol"],
+ [{}, "plain object"],
+ [Temporal.PlainTime, "Temporal.PlainTime, object"],
+ [Temporal.PlainTime.prototype, "Temporal.PlainTime.prototype, object"],
+];
+
+for (const [arg, description] of typeErrorTests) {
+ assert.throws(TypeError, () => instance.since(arg), `${description} is not a valid property bag and does not convert to a string`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
new file mode 100644
index 0000000000..ad80b7ee57
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative time fields are balanced upwards if the argument is given as ZonedDateTime
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-balanceisodatetime step 1:
+ 1. Let _balancedTime_ be ? BalanceTime(_hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_).
+ sec-temporal-builtintimezonegetplaindatetimefor step 3:
+ 3. Set _result_ to ? BalanceISODateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]] + _offsetNanoseconds_).
+ sec-temporal-totemporaltime step 3.b:
+ b. If _item_ has an [[InitializedTemporalZonedDateTime]] internal slot, then
+ ...
+ ii. 1. Set _plainDateTime_ to ? BuiltinTimeZoneGetPlainDateTimeFor(_item_.[[TimeZone]], _instant_, _item_.[[Calendar]]).
+ sec-temporal.plaintime.prototype.since step 3:
+ 3. Set _other_ to ? ToTemporalTime(_other_).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// This code path is encountered if the time zone offset is negative and its
+// absolute value in nanoseconds is greater than the nanosecond field of the
+// exact time's epoch parts
+const tz = TemporalHelpers.specificOffsetTimeZone(-2);
+const datetime = new Temporal.ZonedDateTime(3661_001_001_001n, tz);
+
+const diff = new Temporal.PlainTime().since(datetime);
+
+TemporalHelpers.assertDuration(diff, 0, 0, 0, 0, -1, -1, -1, -1, 0, -999);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
new file mode 100644
index 0000000000..5edc564f07
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch
+info: |
+ sec-temporal-getisopartsfromepoch step 1:
+ 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>.
+ sec-temporal-builtintimezonegetplaindatetimefor step 2:
+ 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]).
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+const datetime = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC");
+
+// This code path shows up anywhere we convert an exact time, before the Unix
+// epoch, with nonzero microseconds or nanoseconds, into a wall time.
+
+const instance = new Temporal.PlainTime(15);
+const result = instance.since(datetime);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, -1, -50, -35, 0, 0, -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
new file mode 100644
index 0000000000..0b73484ed0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
new file mode 100644
index 0000000000..a977d4f8c7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable
+features: [BigInt, Symbol, Temporal, arrow-function]
+---*/
+
+[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => {
+ const timeZone = new Temporal.TimeZone("UTC");
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ timeZone.getOffsetNanosecondsFor = notCallable;
+ assert.throws(
+ TypeError,
+ () => time.since(datetime),
+ `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
new file mode 100644
index 0000000000..1c5998e0bd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown if time zone reports an offset that is out of range
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(RangeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
new file mode 100644
index 0000000000..cec6b03fac
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown if time zone reports an offset that is not a Number
+features: [Temporal]
+includes: [temporalHelpers.js]
+---*/
+
+[
+ undefined,
+ null,
+ true,
+ "+01:00",
+ Symbol(),
+ 3600_000_000_000n,
+ {},
+ { valueOf() { return 3600_000_000_000; } },
+].forEach((wrongOffset) => {
+ const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset);
+ const time = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone);
+ assert.throws(TypeError, () => time.since(datetime));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
new file mode 100644
index 0000000000..78b3f45f33
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/balance-negative-time-units.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative time fields are balanced upwards
+info: |
+ sec-temporal-balancetime steps 3–14:
+ 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000).
+ 4. Set _nanosecond_ to _nanosecond_ modulo 1000.
+ 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000).
+ 6. Set _microsecond_ to _microsecond_ modulo 1000.
+ 7. Set _second_ to _second_ + floor(_millisecond_ / 1000).
+ 8. Set _millisecond_ to _millisecond_ modulo 1000.
+ 9. Set _minute_ to _minute_ + floor(_second_ / 60).
+ 10. Set _second_ to _second_ modulo 60.
+ 11. Set _hour_ to _hour_ + floor(_minute_ / 60).
+ 12. Set _minute_ to _minute_ modulo 60.
+ 13. Let _days_ be floor(_hour_ / 24).
+ 14. Set _hour_ to _hour_ modulo 24.
+ sec-temporal-differencetime step 8:
+ 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_).
+ sec-temporal.plaintime.prototype.since step 12:
+ 12. Let _result_ be ! DifferenceTime(_other_.[[ISOHour]], _other_.[[ISOMinute]], _other_.[[ISOSecond]], _other_.[[ISOMillisecond]], _other_.[[ISOMicrosecond]], _other_.[[ISONanosecond]], _temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]]).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const time = new Temporal.PlainTime(1, 1, 1, 1, 1, 1);
+
+const result1 = time.since(new Temporal.PlainTime(0, 0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance");
+
+const result2 = time.since(new Temporal.PlainTime(0, 0, 0, 0, 2));
+TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance");
+
+const result3 = time.since(new Temporal.PlainTime(0, 0, 0, 2));
+TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance");
+
+const result4 = time.since(new Temporal.PlainTime(0, 0, 2));
+TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance");
+
+const result5 = time.since(new Temporal.PlainTime(0, 2));
+TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance");
+
+// This one is different because hours are later balanced again in BalanceDuration
+const result6 = time.since(new Temporal.PlainTime(2));
+TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js
new file mode 100644
index 0000000000..2c4a6da6ae
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/basic.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.until
+description: Basic usage
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const one = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const two = new Temporal.PlainTime(14, 23, 30, 123, 456, 789);
+const three = new Temporal.PlainTime(13, 30, 30, 123, 456, 789);
+
+TemporalHelpers.assertDuration(one.since(two),
+ 0, 0, 0, 0, /* hours = */ 1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(two.since(one),
+ 0, 0, 0, 0, /* hours = */ -1, 0, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(one.since(three),
+ 0, 0, 0, 0, /* hours = */ 1, 53, 0, 0, 0, 0);
+TemporalHelpers.assertDuration(three.since(one),
+ 0, 0, 0, 0, /* hours = */ -1, -53, 0, 0, 0, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js
new file mode 100644
index 0000000000..58e28d48d8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/branding.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Throw a TypeError if the receiver is invalid
+features: [Symbol, Temporal]
+---*/
+
+const since = Temporal.PlainTime.prototype.since;
+
+assert.sameValue(typeof since, "function");
+
+const args = [new Temporal.PlainTime(12)];
+
+assert.throws(TypeError, () => since.apply(undefined, args), "undefined");
+assert.throws(TypeError, () => since.apply(null, args), "null");
+assert.throws(TypeError, () => since.apply(true, args), "true");
+assert.throws(TypeError, () => since.apply("", args), "empty string");
+assert.throws(TypeError, () => since.apply(Symbol(), args), "symbol");
+assert.throws(TypeError, () => since.apply(1, args), "1");
+assert.throws(TypeError, () => since.apply({}, args), "plain object");
+assert.throws(TypeError, () => since.apply(Temporal.PlainTime, args), "Temporal.PlainTime");
+assert.throws(TypeError, () => since.apply(Temporal.PlainTime.prototype, args), "Temporal.PlainTime.prototype");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/browser.js
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js
new file mode 100644
index 0000000000..b1d7800d31
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/builtin.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Tests that Temporal.PlainTime.prototype.since
+ meets the requirements for built-in objects defined by the
+ introduction of chapter 17 of the ECMAScript Language Specification.
+info: |
+ Built-in functions that are not constructors do not have a "prototype" property unless
+ otherwise specified in the description of a particular function.
+
+ Unless specified otherwise, a built-in object that is callable as a function is a built-in
+ function object with the characteristics described in 10.3. Unless specified otherwise, the
+ [[Extensible]] internal slot of a built-in object initially has the value true.
+
+ Unless otherwise specified every built-in function and every built-in constructor has the
+ Function prototype object [...] as the value of its [[Prototype]] internal slot.
+features: [Temporal]
+---*/
+
+assert.sameValue(Object.isExtensible(Temporal.PlainTime.prototype.since),
+ true, "Built-in objects must be extensible.");
+
+assert.sameValue(Object.prototype.toString.call(Temporal.PlainTime.prototype.since),
+ "[object Function]", "Object.prototype.toString");
+
+assert.sameValue(Object.getPrototypeOf(Temporal.PlainTime.prototype.since),
+ Function.prototype, "prototype");
+
+assert.sameValue(Temporal.PlainTime.prototype.since.hasOwnProperty("prototype"),
+ false, "prototype property");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
new file mode 100644
index 0000000000..730fe43204
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when largestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string"
+];
+for (const largestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit }),
+ `"${largestUnit}" is not a valid value for largestUnit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
new file mode 100644
index 0000000000..b216349ac0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Plural units are accepted as well for the largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => later.since(earlier, { largestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js
new file mode 100644
index 0000000000..340d757b34
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-smallestunit-mismatch.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when smallestUnit is larger than largestUnit
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const units = ["hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"];
+for (let largestIdx = 1; largestIdx < units.length; largestIdx++) {
+ for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) {
+ const largestUnit = units[largestIdx];
+ const smallestUnit = units[smallestIdx];
+ assert.throws(RangeError, () => later.since(earlier, { largestUnit, smallestUnit }));
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
new file mode 100644
index 0000000000..43b1b4a58b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-undefined.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for largestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { largestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default largestUnit is hour");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
new file mode 100644
index 0000000000..f9ecd7f958
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for largestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("largestUnit", "second",
+ (largestUnit) => later.since(earlier, { largestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 3661, 987, 654, 321, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js
new file mode 100644
index 0000000000..61a7c65efe
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/largestunit.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: PlainTime.since with various largestUnit values.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+const fourFortyEight = new Temporal.PlainTime(4, 48, 55);
+const elevenFiftyNine = new Temporal.PlainTime(11, 59, 58);
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit unspecified)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'auto' }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is auto)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'hours' }), 0, 0, 0, 0, 7, 11, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is hours)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'minutes' }), 0, 0, 0, 0, 0, 431, 3, 0, 0, 0, 'does not include higher units than necessary (largest unit is minutes)');
+TemporalHelpers.assertDuration(elevenFiftyNine.since(fourFortyEight, { largestUnit: 'seconds' }), 0, 0, 0, 0, 0, 0, 25863, 0, 0, 0, 'does not include higher units than necessary (largest unit is seconds)');
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js
new file mode 100644
index 0000000000..428352a5f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/leap-second.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Leap second is a valid ISO string for PlainTime
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(23, 59, 59);
+
+let arg = "2016-12-31T23:59:60";
+const result1 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "leap second is a valid ISO string for PlainTime"
+);
+
+arg = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 };
+const result2 = instance.since(arg);
+TemporalHelpers.assertDuration(
+ result2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "second: 60 is ignored in property bag for PlainTime"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js
new file mode 100644
index 0000000000..1905d55029
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/length.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2020 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: Temporal.PlainTime.prototype.since.length is 1
+info: |
+ Every built-in function object, including constructors, has a "length" property whose value is
+ an integer. Unless otherwise specified, this value is equal to the largest number of named
+ arguments shown in the subclause headings for the function description. Optional parameters
+ (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form
+ «...name») are not included in the default argument count.
+
+ Unless otherwise specified, the "length" property of a built-in function object has the
+ attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.since, "length", {
+ value: 1,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js
new file mode 100644
index 0000000000..5ba97cebbb
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/name.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Temporal.PlainTime.prototype.since.name is "since".
+info: |
+ Every built-in function object, including constructors, that is not identified as an anonymous
+ function has a "name" property whose value is a String. Unless otherwise specified, this value
+ is the name that is given to the function in this specification.
+
+ Unless otherwise specified, the "name" property of a built-in function object, if it exists,
+ has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+verifyProperty(Temporal.PlainTime.prototype.since, "name", {
+ value: "since",
+ writable: false,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
new file mode 100644
index 0000000000..43b9cd80f9
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/not-a-constructor.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: >
+ Temporal.PlainTime.prototype.since does not implement [[Construct]], is not new-able
+info: |
+ Built-in function objects that are not identified as constructors do not implement the
+ [[Construct]] internal method unless otherwise specified in the description of a particular
+ function.
+includes: [isConstructor.js]
+features: [Reflect.construct, Temporal]
+---*/
+
+assert.throws(TypeError, () => {
+ new Temporal.PlainTime.prototype.since();
+}, "Calling as constructor");
+
+assert.sameValue(isConstructor(Temporal.PlainTime.prototype.since), false,
+ "isConstructor(Temporal.PlainTime.prototype.since)");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js
new file mode 100644
index 0000000000..da9c831983
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-invalid.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown when a primitive is passed as the options argument
+features: [Temporal]
+---*/
+
+const values = [null, true, "hello", Symbol("foo"), 1, 1n];
+const time = new Temporal.PlainTime(15, 23, 30, 123, 456, 789);
+const one = new Temporal.PlainTime(16, 23, 30, 123, 456, 789);
+
+for (const badOptions of values) {
+ assert.throws(TypeError, () => time.since(one, badOptions));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js
new file mode 100644
index 0000000000..1212676bd4
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-object.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Empty or a function object may be used as options
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime();
+
+const result1 = instance.since(new Temporal.PlainTime(12, 34, 56), {});
+TemporalHelpers.assertDuration(
+ result1, 0, 0, 0, 0, -12, -34, -56, 0, 0, 0,
+ "options may be an empty plain object"
+);
+
+const result2 = instance.since(new Temporal.PlainTime(12, 34, 56), () => {});
+TemporalHelpers.assertDuration(
+ result2, 0, 0, 0, 0, -12, -34, -56, 0, 0, 0,
+ "options may be a function object"
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
new file mode 100644
index 0000000000..e31734100d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Verify that undefined options are handled correctly.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(18, 34, 56, 987, 654, 322);
+
+const explicit = later.since(earlier, undefined);
+assert.sameValue(explicit.hours, 6, "default largest unit is hours");
+assert.sameValue(explicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+const implicit = later.since(earlier);
+assert.sameValue(implicit.hours, 6, "default largest unit is hours");
+assert.sameValue(implicit.nanoseconds, 1, "default smallest unit is nanoseconds and no rounding");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js
new file mode 100644
index 0000000000..78e17fa2f8
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/options-wrong-type.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: TypeError thrown when options argument is a primitive
+features: [BigInt, Symbol, Temporal]
+---*/
+
+const badOptions = [
+ null,
+ true,
+ "some string",
+ Symbol(),
+ 1,
+ 2n,
+];
+
+const instance = new Temporal.PlainTime();
+for (const value of badOptions) {
+ assert.throws(TypeError, () => instance.since(new Temporal.PlainTime(12, 34, 56), value),
+ `TypeError on wrong options type ${typeof value}`);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js
new file mode 100644
index 0000000000..33f3b9bac5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/order-of-operations.js
@@ -0,0 +1,95 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Properties on an object passed to since() are accessed in the correct order
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const expected = [
+ // ToTemporalTime
+ "get other.hour",
+ "get other.hour.valueOf",
+ "call other.hour.valueOf",
+ "get other.microsecond",
+ "get other.microsecond.valueOf",
+ "call other.microsecond.valueOf",
+ "get other.millisecond",
+ "get other.millisecond.valueOf",
+ "call other.millisecond.valueOf",
+ "get other.minute",
+ "get other.minute.valueOf",
+ "call other.minute.valueOf",
+ "get other.nanosecond",
+ "get other.nanosecond.valueOf",
+ "call other.nanosecond.valueOf",
+ "get other.second",
+ "get other.second.valueOf",
+ "call other.second.valueOf",
+ // CopyDataProperties
+ "ownKeys options",
+ "getOwnPropertyDescriptor options.roundingIncrement",
+ "get options.roundingIncrement",
+ "getOwnPropertyDescriptor options.roundingMode",
+ "get options.roundingMode",
+ "getOwnPropertyDescriptor options.largestUnit",
+ "get options.largestUnit",
+ "getOwnPropertyDescriptor options.smallestUnit",
+ "get options.smallestUnit",
+ "getOwnPropertyDescriptor options.additional",
+ "get options.additional",
+ // GetDifferenceSettings
+ "get options.largestUnit.toString",
+ "call options.largestUnit.toString",
+ "get options.roundingIncrement.valueOf",
+ "call options.roundingIncrement.valueOf",
+ "get options.roundingMode.toString",
+ "call options.roundingMode.toString",
+ "get options.smallestUnit.toString",
+ "call options.smallestUnit.toString",
+];
+const actual = [];
+
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+
+const other = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 1.7,
+ minute: 1.7,
+ second: 1.7,
+ millisecond: 1.7,
+ microsecond: 1.7,
+ nanosecond: 1.7,
+ calendar: "iso8601",
+}, "other");
+
+const options = TemporalHelpers.propertyBagObserver(actual, {
+ roundingIncrement: 1,
+ roundingMode: "trunc",
+ largestUnit: "hours",
+ smallestUnit: "nanoseconds",
+ additional: true,
+}, "options");
+
+const result = instance.since(other, options);
+assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+// short-circuit does not skip reading options
+const identicalPropertyBag = TemporalHelpers.propertyBagObserver(actual, {
+ hour: 12,
+ minute: 34,
+ second: 56,
+ millisecond: 987,
+ microsecond: 654,
+ nanosecond: 321,
+}, "other");
+instance.since(identicalPropertyBag, options);
+assert.compareArray(actual, expected, "order of operations with identical times");
+
+actual.splice(0); // clear
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js
new file mode 100644
index 0000000000..563d9cd8b5
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/plaintime-propertybag-no-time-units.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Missing time units in property bag default to 0
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const instance = new Temporal.PlainTime(1, 0, 0, 0, 0, 1);
+
+const props = {};
+assert.throws(TypeError, () => instance.since(props), "TypeError if no properties are present");
+
+props.minute = 30;
+const result = instance.since(props);
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 30, 0, 0, 0, 1, "missing time units default to 0");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
new file mode 100644
index 0000000000..1487fef147
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/prop-desc.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: The "since" property of Temporal.PlainTime.prototype
+includes: [propertyHelper.js]
+features: [Temporal]
+---*/
+
+assert.sameValue(
+ typeof Temporal.PlainTime.prototype.since,
+ "function",
+ "`typeof PlainTime.prototype.since` is `function`"
+);
+
+verifyProperty(Temporal.PlainTime.prototype, "since", {
+ writable: true,
+ enumerable: false,
+ configurable: true,
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js
new file mode 100644
index 0000000000..8821442df1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/result-sub-second.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Supports sub-second precision
+includes: [temporalHelpers.js]
+features: [Temporal, arrow-function]
+---*/
+
+const time1 = Temporal.PlainTime.from("10:23:15");
+const time2 = Temporal.PlainTime.from("17:15:57.250250250");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 24762250, 250, 250, "milliseconds");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "microseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 24762250250, 250, "microseconds");
+
+TemporalHelpers.assertDuration(time2.since(time1, { largestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 0, 0, 0, /* milliseconds = */ 0, 0, 24762250250250, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js
new file mode 100644
index 0000000000..7d6be19a63
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/round-cross-unit-boundary.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Rounding can cross unit boundaries up to largestUnit
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime();
+const later = new Temporal.PlainTime(1, 59, 59);
+const duration = earlier.since(later, { largestUnit: "hours", smallestUnit: "minutes", roundingMode: "expand" });
+TemporalHelpers.assertDuration(duration, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, "-1:60 balances to -2 hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js
new file mode 100644
index 0000000000..1318ce0a71
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-hours.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "hours");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js
new file mode 100644
index 0000000000..e1a03301e1
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-invalid.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests roundingIncrement restrictions.
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "hours", roundingIncrement: 11 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 29 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "hours", roundingIncrement: 24 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 60 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 60 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 1000 }));
+assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 1000 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js
new file mode 100644
index 0000000000..1013fb750e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-microseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 196, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 195, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 192, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 190, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 180, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 175, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 160, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 150, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 100, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 125, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "microseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js
new file mode 100644
index 0000000000..5a74a65fea
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-milliseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 860, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 840, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 850, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 800, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 750, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 500, 0, 0, "milliseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js
new file mode 100644
index 0000000000..e765503076
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-minutes.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 34, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 33, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 32, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 24, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 20, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 30, 0, 0, 0, 0, "minutes");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
new file mode 100644
index 0000000000..62bfdb33d2
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nan.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingIncrement option is NaN
+info: |
+ sec-getoption step 8.b:
+ b. If _value_ is *NaN*, throw a *RangeError* exception.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: NaN }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js
new file mode 100644
index 0000000000..aa17ab168c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-nanoseconds.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 533, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 8 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 528, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 530, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 25 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 525, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 40 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 520, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 50 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 100 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 125 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 200 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 400, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 250 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingIncrement: 500 }),
+ 0, 0, 0, 0, 10, 35, 23, 865, 198, 500, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
new file mode 100644
index 0000000000..f728a4c75f
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-non-integer.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Rounding for roundingIncrement option
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>đť”˝</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(â„ť(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+const result = later.since(earlier, { roundingIncrement: 2.5, roundingMode: "trunc" });
+TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, "roundingIncrement 2.5 truncates to 2");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
new file mode 100644
index 0000000000..74ad0e56dd
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-out-of-range.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingIncrement option out of range
+info: |
+ ToTemporalRoundingIncrement ( _normalizedOptions_ )
+
+ 1. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, *"number"*, *undefined*, *1*<sub>đť”˝</sub>).
+ 2. If _increment_ is not finite, throw a *RangeError* exception.
+ 3. Let _integerIncrement_ be truncate(â„ť(_increment_)).
+ 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception.
+ 5. Return _integerIncrement_.
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(12, 34, 56, 0, 0, 5);
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -Infinity }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: -1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 0.9 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: 1e9 + 1 }));
+assert.throws(RangeError, () => later.since(earlier, { roundingIncrement: Infinity }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js
new file mode 100644
index 0000000000..48ae41c282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-seconds.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Valid values for roundingIncrement option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(3, 12, 34, 123, 456, 789);
+const later = new Temporal.PlainTime(13, 47, 57, 988, 655, 322);
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 1 }),
+ 0, 0, 0, 0, 10, 35, 23, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 2 }),
+ 0, 0, 0, 0, 10, 35, 22, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 3 }),
+ 0, 0, 0, 0, 10, 35, 21, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 4 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 5 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 6 }),
+ 0, 0, 0, 0, 10, 35, 18, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 10 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 12 }),
+ 0, 0, 0, 0, 10, 35, 12, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 15 }),
+ 0, 0, 0, 0, 10, 35, 15, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 20 }),
+ 0, 0, 0, 0, 10, 35, 20, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingIncrement: 30 }),
+ 0, 0, 0, 0, 10, 35, 0, 0, 0, 0, "seconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
new file mode 100644
index 0000000000..816763660e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-undefined.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for roundingIncrement option
+info: |
+ sec-getoption step 3:
+ 3. If _value_ is *undefined*, return _fallback_.
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+const explicit = later.since(earlier, { roundingIncrement: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "default roundingIncrement is 1");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
new file mode 100644
index 0000000000..a8d580d49a
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingincrement-wrong-type.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for roundingIncrement option
+info: |
+ sec-getoption step 8.a:
+ a. Set _value_ to ? ToNumber(value).
+ sec-temporal-totemporalroundingincrement step 5:
+ 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1).
+ sec-temporal.plaintime.prototype.since step 11:
+ 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*).
+includes: [temporalHelpers.js, compareArray.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+
+TemporalHelpers.checkRoundingIncrementOptionWrongType(
+ (roundingIncrement) => later.since(earlier, { roundingIncrement }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, descr),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js
new file mode 100644
index 0000000000..2c59d6364e
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-ceil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "ceil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "ceil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js
new file mode 100644
index 0000000000..fc0ffaf8e0
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-expand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "expand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 5], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 18], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 865], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "expand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js
new file mode 100644
index 0000000000..2063878a22
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-floor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "floor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -5]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -18]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -865]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "floor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js
new file mode 100644
index 0000000000..319a618aed
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfCeil.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfCeil".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfCeil";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js
new file mode 100644
index 0000000000..b6b3edbf50
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfEven.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfEven".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfEven";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js
new file mode 100644
index 0000000000..a44ce32e5c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfExpand.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfExpand".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 198], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfExpand";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js
new file mode 100644
index 0000000000..560047cf1d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfFloor.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfFloor".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -198]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfFloor";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js
new file mode 100644
index 0000000000..9658dfc732
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-halfTrunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "halfTrunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 5], [0, 0, 0, 0, -4, -17, -5]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "halfTrunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
new file mode 100644
index 0000000000..ba57777e2c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-invalid-string.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when roundingMode option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit: "microsecond", roundingMode }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js
new file mode 100644
index 0000000000..e1b447c453
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-trunc.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Tests calculations with roundingMode "trunc".
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(8, 22, 36, 123, 456, 789);
+const later = new Temporal.PlainTime(12, 39, 40, 987, 654, 289);
+
+const expected = [
+ ["hours", [0, 0, 0, 0, 4], [0, 0, 0, 0, -4]],
+ ["minutes", [0, 0, 0, 0, 4, 17], [0, 0, 0, 0, -4, -17]],
+ ["seconds", [0, 0, 0, 0, 4, 17, 4], [0, 0, 0, 0, -4, -17, -4]],
+ ["milliseconds", [0, 0, 0, 0, 4, 17, 4, 864], [0, 0, 0, 0, -4, -17, -4, -864]],
+ ["microseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197], [0, 0, 0, 0, -4, -17, -4, -864, -197]],
+ ["nanoseconds", [0, 0, 0, 0, 4, 17, 4, 864, 197, 500], [0, 0, 0, 0, -4, -17, -4, -864, -197, -500]],
+];
+
+const roundingMode = "trunc";
+
+expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => {
+ const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive;
+ const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative;
+ TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit, roundingMode }),
+ py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns,
+ `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)`
+ );
+ TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit, roundingMode }),
+ ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns,
+ `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)`
+ );
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
new file mode 100644
index 0000000000..f437428f15
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-undefined.js
@@ -0,0 +1,93 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for roundingMode option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = Temporal.PlainTime.from("08:22:36.123456789");
+const later = Temporal.PlainTime.from("12:39:40.987654321");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "hours", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "hours" }),
+ 0, 0, 0, 0, -4, 0, 0, 0, 0, 0, "hours");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, 4, 17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "minutes", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "minutes" }),
+ 0, 0, 0, 0, -4, -17, 0, 0, 0, 0, "minutes");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "seconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "seconds" }),
+ 0, 0, 0, 0, -4, -17, -4, 0, 0, 0, "seconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "milliseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "milliseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, 0, 0, "milliseconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "microseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "microseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, 0, "microseconds");
+
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ later.since(earlier, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, 4, 17, 4, 864, 197, 532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "nanoseconds", roundingMode: undefined }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+TemporalHelpers.assertDuration(
+ earlier.since(later, { smallestUnit: "nanoseconds" }),
+ 0, 0, 0, 0, -4, -17, -4, -864, -197, -532, "nanoseconds");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
new file mode 100644
index 0000000000..3ccf979f0d
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/roundingmode-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for roundingMode option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 123, 987, 500);
+TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc",
+ (roundingMode) => later.since(earlier, { smallestUnit: "microsecond", roundingMode }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 123, 987, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js
new file mode 100644
index 0000000000..eda1477282
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/shell.js
@@ -0,0 +1,24 @@
+// GENERATED, DO NOT EDIT
+// file: isConstructor.js
+// Copyright (C) 2017 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: |
+ Test if a given function is a constructor function.
+defines: [isConstructor]
+features: [Reflect.construct]
+---*/
+
+function isConstructor(f) {
+ if (typeof f !== "function") {
+ throw new Test262Error("isConstructor invoked with a non-function value");
+ }
+
+ try {
+ Reflect.construct(function(){}, [], f);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
new file mode 100644
index 0000000000..28dd00c591
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-invalid-string.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: RangeError thrown when smallestUnit option not one of the allowed string values
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+const badValues = [
+ "era",
+ "eraYear",
+ "year",
+ "month",
+ "week",
+ "day",
+ "millisecond\0",
+ "mill\u0131second",
+ "SECOND",
+ "eras",
+ "eraYears",
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "milliseconds\0",
+ "mill\u0131seconds",
+ "SECONDS",
+ "other string",
+];
+for (const smallestUnit of badValues) {
+ assert.throws(RangeError, () => later.since(earlier, { smallestUnit }),
+ `"${smallestUnit}" is not a valid value for smallest unit`);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
new file mode 100644
index 0000000000..c6d998214b
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-plurals-accepted.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Plural units are accepted as well for the smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+const later = new Temporal.PlainTime(13, 35, 57, 988, 655, 322);
+const validUnits = [
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond",
+];
+TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => later.since(earlier, { smallestUnit }), validUnits);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
new file mode 100644
index 0000000000..ba00b5348c
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-undefined.js
@@ -0,0 +1,22 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Fallback value for smallestUnit option
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+
+const explicit = later.since(earlier, { smallestUnit: undefined });
+TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const implicit = later.since(earlier, {});
+TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+const lambda = later.since(earlier, () => {});
+TemporalHelpers.assertDuration(lambda, 0, 0, 0, 0, 1, 1, 1, 987, 654, 321, "default smallestUnit is nanosecond");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
new file mode 100644
index 0000000000..915b81a772
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/smallestunit-wrong-type.js
@@ -0,0 +1,19 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Type conversions for smallestUnit option
+includes: [compareArray.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+const earlier = new Temporal.PlainTime(12, 34, 56, 0, 0, 0);
+const later = new Temporal.PlainTime(13, 35, 57, 987, 654, 321);
+TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond",
+ (smallestUnit) => later.since(earlier, { smallestUnit }),
+ (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 1, 1, 1, 987, 654, 0, descr),
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js
new file mode 100644
index 0000000000..86f60c8ed7
--- /dev/null
+++ b/js/src/tests/test262/built-ins/Temporal/PlainTime/prototype/since/year-zero.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2022 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal.plaintime.prototype.since
+description: Negative zero, as an extended year, is rejected
+features: [Temporal, arrow-function]
+---*/
+
+const invalidStrings = [
+ "-000000-12-07T03:24:30",
+ "-000000-12-07T03:24:30+01:00",
+ "-000000-12-07T03:24:30+00:00[UTC]",
+];
+const instance = new Temporal.PlainTime(12, 34, 56, 987, 654, 321);
+invalidStrings.forEach((arg) => {
+ assert.throws(
+ RangeError,
+ () => instance.since(arg),
+ "reject minus zero as extended year"
+ );
+});
+
+reportCompare(0, 0);